Dynamics 365

Dynamics 365 Integration: REST vs OData vs SDK

12 min read
#dynamics-365#integration#rest-api#odata
Georg Pfeiffer
Georg Pfeiffer
Senior Solution Architect

Integrating with Dynamics 365 means choosing between REST, OData, and the SDK. Each has different performance characteristics, authentication models, and trade-offs. After years of building integrations with Dynamics 365, here's my field guide.

The Three Approaches

REST/Web APIODataSDK (IOrganizationService)
**Protocol**HTTP + JSONHTTP + JSON (OData conventions).NET SDK (HTTP under the hood)
**Auth**OAuth 2.0 / Azure ADOAuth 2.0 / Azure ADConnection string or OAuth
**Batching**`$batch` endpoint`$batch` endpoint`ExecuteMultipleRequest`
**Change tracking**Manual`odata.track-changes``RetrieveEntityChanges`
**Best for**Non-.NET clientsComplex queries, filtering.NET plugins, workflows

REST/Web API: Maximum Flexibility

The Web API works from any language and platform. It's the right choice for non-.NET integrations or when you need full control over HTTP requests:

csharp
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", accessToken);

var response = await client.GetAsync(
    $"{crmUrl}/api/data/v9.2/contacts?" +
    "$select=fullname,emailaddress1" +
    "&$filter=statecode eq 0" +
    "&$top=100");

var result = await response.Content
    .ReadFromJsonAsync<ODataResponse<Contact>>();

**Performance tip:** Always use `$select` to limit returned fields. A full contact record can be 5KB+ of JSON. With `$select`, it's a few hundred bytes.

OData: Query Power

OData adds powerful query capabilities on top of REST. Complex filters, aggregations, and joins are possible without multiple roundtrips:

text
GET /api/data/v9.2/accounts?
  $select=name,revenue
  &$expand=contact_customer_accounts(
    $select=fullname,emailaddress1;
    $filter=statecode eq 0
  )
  &$filter=revenue gt 1000000
  &$orderby=revenue desc
  &$top=50

This single request retrieves high-value accounts with their active contacts — something that would require multiple queries with the SDK.

FetchXML via Web API

For even more complex queries, you can pass FetchXML through the Web API:

csharp
var fetchXml = @"
<fetch top='50' aggregate='true'>
  <entity name='opportunity'>
    <attribute name='estimatedvalue' alias='total'
               aggregate='sum' />
    <attribute name='ownerid' alias='owner'
               groupby='true' />
    <filter>
      <condition attribute='statecode' operator='eq'
                 value='0' />
    </filter>
  </entity>
</fetch>";

var encoded = Uri.EscapeDataString(fetchXml);
var response = await client.GetAsync(
    $"{crmUrl}/api/data/v9.2/opportunities?fetchXml={encoded}");

SDK: The .NET Native Choice

Inside Dynamics 365 plugins and custom workflow activities, the SDK is the only option. Outside of Dynamics, it's still the most ergonomic choice for .NET:

csharp
var service = new ServiceClient(connectionString);

var query = new QueryExpression("contact")
{
    ColumnSet = new ColumnSet("fullname", "emailaddress1"),
    Criteria =
    {
        Conditions =
        {
            new ConditionExpression(
                "statecode", ConditionOperator.Equal, 0)
        }
    },
    TopCount = 100
};

var contacts = service.RetrieveMultiple(query);

Batch Operations With ExecuteMultiple

For bulk operations, `ExecuteMultipleRequest` is essential. Without it, creating 1000 records means 1000 HTTP roundtrips:

csharp
var batch = new ExecuteMultipleRequest
{
    Requests = new OrganizationRequestCollection(),
    Settings = new ExecuteMultipleSettings
    {
        ContinueOnError = true,
        ReturnResponses = false
    }
};

foreach (var contact in contacts)
{
    batch.Requests.Add(new CreateRequest { Target = contact });

    if (batch.Requests.Count >= 200)
    {
        service.Execute(batch);
        batch.Requests.Clear();
    }
}

if (batch.Requests.Count > 0)
    service.Execute(batch);

**Important:** Keep batch sizes at 200 or fewer. Larger batches risk timeouts and are harder to retry on partial failure.

My Decision Framework

  1. **Building a plugin or workflow?** → SDK, no choice
  2. **Non-.NET client (Python, Node)?** → REST/Web API
  3. **Complex queries with joins/aggregations?** → OData or FetchXML via Web API
  4. **Bulk data operations in .NET?** → SDK with ExecuteMultiple
  5. **Simple CRUD from .NET?** → SDK for ergonomics, REST for portability

Performance Comparison

From benchmarks on a real Dynamics 365 Online instance:

OperationRESTODataSDK
Single read120ms125ms130ms
Filtered list (100)180ms175ms190ms
Create single250ms250ms260ms
Batch create (200)3.2s3.1s2.8s

The differences are marginal for single operations. The SDK has a slight edge in batch operations due to optimized serialization.

Key Takeaways

  • All three approaches use HTTP under the hood — performance differences are minimal
  • Use `$select` always, batch always — these matter more than which API you choose
  • OData's query capabilities can eliminate multiple roundtrips
  • The SDK is mandatory for plugins and the best ergonomic choice for .NET
  • Pick based on your platform, query complexity, and team familiarity

Share this article

Georg Pfeiffer

About the Author

Georg is a senior solution architect specializing in .NET, Azure, and Dynamics 365. He helps organizations design and build scalable, maintainable enterprise systems. When he's not writing code, he's writing about it here.

Learn more about Georg

© 2026 georgpfeiffer.dev. All rights reserved.

Built with SvelteKit & Tailwind CSS