Key Takeaways
The Microsoft Dynamics 365 Web API is a RESTful web service that gives developers programmatic access to Dynamics 365 data and functionality over HTTPS. It is the standard integration layer for both CRM (Customer Relationship Management) and ERP (Enterprise Resource Planning) workloads inside the Dynamics 365 platform.
The Web API is built on OData v4, an OASIS standard for building and consuming RESTful APIs. Because it follows open standards, the API is compatible with virtually any programming language or platform, including C#, JavaScript, Python, and Java. Developers use standard HTTP verbs – GET, POST, PATCH, and DELETE – to retrieve, create, update, and remove records.
Unlike the older Organization Service (SOAP-based), the Web API is the recommended approach for all new integrations. It runs on the same underlying Dataverse platform that powers Microsoft Power Apps and Power Automate, so capabilities added to Dataverse are immediately accessible through the Web API.
As of time of writing, all modern implementations use API version v9.2. The path follows this pattern:
https://{organization}.api.crm.dynamics.com/api/data/v9.2/{resource}
Version 8.2, which appeared in documentation published in 2016 and 2017, is outdated. Microsoft replaced it with the v9.x series, which introduced significant improvements to batch processing, metadata handling, and OData compliance. Use v9.2 for all new work.
The Dynamics 365 Web API gives developers and IT teams a single, standards-based interface for everything they need to do with Dynamics 365 data. Below are the four primary use cases.
The Web API makes it straightforward to connect Dynamics 365 with external systems, e.g., marketing platforms, ERP systems, ecommerce solutions, or proprietary line-of-business applications. Because it follows REST and OData standards, any platform that can make an HTTPS request can integrate with Dynamics 365 without a custom connector.
Common integrations include:
Manual data entry and repetitive administrative tasks are prime candidates for automation through the Dynamics 365 API. Teams commonly use it to do things like auto-create records when events occur in connected systems, run scheduled data synchronization jobs between Dynamics 365 and external databases, and trigger follow-up tasks in Dynamics CRM based on actions taken in other applications.
Power Automate integration makes this accessible to non-developers: flows can call the Dynamics 365 REST API endpoints without writing code, while developers can build more complex automation logic in C# or Python.
Many organizations need functionality that goes beyond the standard Dynamics 365 interface. The Web API is the foundation for custom portals, mobile apps, and internal tools that surface Dynamics 365 data in tailored experiences. Developers can build customer-facing portals that read and write CRM records in real time, internal dashboards that pull Dynamics 365 data into custom reporting views, and mobile applications that give field teams access to account and contact records without opening the full Dynamics interface.
The Web API enforces OAuth 2.0 authentication for every request. This means applications never handle user passwords directly. Instead, they exchange client credentials for a short-lived access token issued by Microsoft Entra ID, which is included as a bearer token in each API call. Dynamics 365 security roles control what each authenticated identity can read or modify, so the API respects your existing role-based access control configuration automatically.
Before making your first API call, four things need to be in place.
Microsoft Entra ID is the identity platform that issues OAuth access tokens for Dynamics 365 API requests. Your organization needs an active Microsoft Entra ID tenant, which is included with any Microsoft 365 or Dynamics 365 subscription. You will use the Entra ID portal to register your application and manage its permissions. Your organization may still refer to this as Azure Active Directory (Azure AD), so you should know that both terms refer to the same service; Microsoft rebranded it to Microsoft Entra ID in 2023.
You need an active Dynamics 365 environment (also called an instance or organization). This can be a production environment, a sandbox, or a developer environment. Free developer environments are available through the Microsoft Power Apps Developer Plan, which is a practical option for testing and learning. Note your environment’s URL, which will be the base of all your API requests. It follows this format:
https://{your-org-name}.api.crm.dynamics.com
API calls made from code (server-to-server integrations, scripts, background services) require a registered application in Microsoft Entra ID. The registration process assigns your application a client ID and lets you configure a client secret or certificate for authentication. Delegated permissions work differently, since they allow an application to make API calls on behalf of a signed-in user, but application permissions are more common for integrations and automation.
The registered application needs the Dynamics CRM permission user_impersonation (for delegated access) or appropriate application permissions granted in Entra ID. In addition, the Dynamics 365 user account associated with your integration needs the appropriate security roles assigned inside Dynamics 365 itself. API authentication succeeding does not automatically grant access to all data, since Dynamics 365 role-based access control applies to every record the API returns or modifies.
Authentication to the Dynamics 365 Web API uses the OAuth 2.0 client credentials flow (for server-to-server integrations) or the authorization code flow (for user-delegated access). The steps below cover the client credentials approach, which is the most common for integrations.
Navigate to the Microsoft Entra admin center (https://entra.microsoft.com) and select App registrations. Click New registration, give your application a name, and choose the appropriate supported account type (typically “Single tenant” for internal integrations). You do not need a redirect URI for the client credentials flow.
After registration, the Overview page shows your Application (client) ID and Directory (tenant) ID – copy both. Then navigate to Certificates & secrets, click New client secret, set an expiration period, and copy the value immediately. You cannot retrieve the secret value after you leave the page. Store it securely in Azure Key Vault or an equivalent secrets manager, never in source code.
In your app registration, go to API permissions and click Add a permission. Select Dynamics CRM, choose user_impersonation (delegated) or the appropriate application permission, and click Add permissions. For application-level access, click Grant admin consent for your tenant. You also need to add your application as a user inside Dynamics 365 under Settings > Users.
Exchange your client credentials for an access token by sending a POST request to the Microsoft identity platform token endpoint:
POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Include the following form-encoded body parameters:
client_id={your-client-id}
client_secret={your-client-secret}
scope=https://{your-org}.api.crm.dynamics.com/.default
grant_type=client_credentials
The response returns a JSON object containing an access_token value. Include this token as a Bearer token in the Authorization header of every subsequent API request:
Authorization: Bearer {access_token}
Access tokens expire after approximately one hour. Your application should handle token refresh automatically by re-requesting a token when a 401 response is received.
Postman is the fastest way to test the Dynamics 365 Web API without writing code. To get started, create a new request collection, set up an OAuth 2.0 authorization configuration with your tenant ID, client ID, and client secret, and point it at the token URL above. Once authenticated, you can send GET requests directly to your Dynamics 365 environment URL and inspect the responses.
In Postman, set the Authorization type to OAuth 2.0 and configure a new token using the client credentials grant type. Postman should handle the token exchange automatically and attach the bearer token to each request. This lets you iterate on query parameters and inspect raw JSON responses before implementing the same logic in code.
The following request retrieves the name field from the first three account records in Dynamics 365:
GET https://{org}.api.crm.dynamics.com/api/data/v9.2/accounts?$select=name&$top=3
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
Authorization: Bearer {access_token}
A successful response returns HTTP 200 with a JSON body. The value array contains the matching records:
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "https://{org}.api.crm.dynamics.com/api/data/v9.2/$metadata#accounts(name)",
"value": [
{
"@odata.etag": "W/\"502000\"",
"name": "Contoso Ltd",
"accountid": "89390c24-9c72-e511-80d4-00155d2a68d1"
},
{
"@odata.etag": "W/\"502001\"",
"name": "Fabrikam Inc",
"accountid": "8b390c24-9c72-e511-80d4-00155d2a68d1"
}
]
}
The @odata.context value confirms the endpoint and selected fields. The @odata.etag on each record is used for concurrency control when updating records with PATCH. If the result set is paginated, an @odata.nextLink property appears at the root level with the URL for the next page.
The Dynamics 365 Web API uses five standard HTTP methods to perform all data operations:
|
Method |
Purpose |
Common Use |
|
GET |
Retrieve records |
Query accounts, contacts, leads, custom entities |
|
POST |
Create records |
Create new accounts, cases, opportunities |
|
PATCH |
Update records |
Update fields on existing records; also used for upsert |
|
DELETE |
Delete records |
Remove a record or clear a single property |
|
PUT |
Replace single property |
Update a single entity attribute directly |
All requests must include the Accept: application/json and OData-MaxVersion: 4.0 headers. Requests that include a body (POST, PATCH) also require Content-Type: application/json.
OData query options are the primary mechanism for filtering, shaping, and sorting the data the Web API returns. They are appended to the entity set URL as query string parameters. It is possible to combine multiple options in a single request.
$select limits the response to specific columns, reducing payload size and improving performance. Without it, the API returns all fields on every record, which can be slow and bandwidth-intensive.
GET /api/data/v9.2/contacts?$select=firstname,lastname,emailaddress1
This returns only the first name, last name, and email address for each contact record instead of all 80+ contact fields.
$filter applies conditions to limit which records are returned. It supports standard OData comparison operators (i.e., eq, ne, gt, lt, ge, le) and logical operators (i.e., and, or, not).
GET /api/data/v9.2/accounts?$select=name,revenue&$filter=revenue gt 1000000
This retrieves accounts with annual revenue over $1,000,000. You can combine conditions:
GET /api/data/v9.2/leads?$select=fullname,statuscode&$filter=statuscode eq 1 and createdon gt 2024-01-01
String functions are also available: contains(name,'Contoso'), startswith(name,'A'), endswith(emailaddress1,'microsoft.com').
$orderby sorts the result set by one or more fields. Append asc or desc to specify direction. The default is ascending.
GET /api/data/v9.2/opportunities?$select=name,estimatedvalue&$orderby=estimatedvalue desc
Sort by multiple fields by separating them with commas:
GET /api/data/v9.2/contacts?$select=lastname,firstname&$orderby=lastname asc,firstname asc
$top limits the number of records returned. The Dynamics 365 Web API returns a maximum of 5,000 records per page by default. Use $top to retrieve a smaller set.
GET /api/data/v9.2/accounts?$select=name&$top=10
For larger data sets, use the Prefer: odata.maxpagesize={n} header to set your preferred page size, then follow the @odata.nextLink URL in each response to retrieve subsequent pages:
GET /api/data/v9.2/accounts?$select=name
Prefer: odata.maxpagesize=50
If the result set has more records than the page size, the response includes an @odata.nextLink property with the URL for the next page. Issue a new GET request to that URL to continue paginating.
$expand retrieves related records in a single request, similar to a JOIN in SQL. It follows navigation properties defined in the Dynamics 365 metadata.
GET /api/data/v9.2/accounts?$select=name&$expand=primarycontactid($select=fullname,emailaddress1)
This returns each account’s name along with the full name and email of its primary contact, all in one request. You can combine $expand with $filter and $select on the expanded entity:
GET /api/data/v9.2/opportunities?$select=name,estimatedvalue
&$expand=customerid_account($select=name,revenue;$filter=revenue gt 500000)
Send a POST request to the entity set endpoint with a JSON body containing the fields to populate. The response is HTTP 204 with no body by default, but you can request the full created record by including the Prefer: return=representation header.
POST /api/data/v9.2/accounts HTTP/1.1
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json
{
"name": "Contoso Ltd",
"creditonhold": false,
"revenue": 5000000,
"description": "New account created via Web API"
}
A successful create returns HTTP 204 No Content. The OData-EntityId response header contains the URI of the newly created record, including its GUID. To receive the full record in the response body with a 201 Created status code, add the Prefer: return=representation header.
PATCH updates only the fields included in the request body, leaving all other fields unchanged. Target the specific record by appending its GUID to the entity set URL.
PATCH /api/data/v9.2/accounts(7eb682f1-ca75-e511-80d4-00155d2a68d1) HTTP/1.1
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"revenue": 7500000,
"description": "Updated via Web API"
}
A successful PATCH returns HTTP 204 No Content. To prevent overwriting concurrent changes, include an If-Match header with the record’s current ETag value. If the record has been modified since you last retrieved it, the API returns 412 Precondition Failed.
PATCH can also perform an upsert: if the specified GUID does not exist, the API creates the record. Include If-Match: * to update only (fail if not found), or If-None-Match: * to create only (fail if already exists).
Send a DELETE request targeting the record URI. A successful delete returns HTTP 204 No Content.
DELETE /api/data/v9.2/accounts(7eb682f1-ca75-e511-80d4-00155d2a68d1) HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
To delete a single property value rather than the entire record, append the property name to the URI:
DELETE /api/data/v9.2/accounts(7eb682f1-ca75-e511-80d4-00155d2a68d1)/description
|
Error Code |
Meaning |
Common Cause |
Solution |
|
401 Unauthorized |
Authentication failed |
Expired or invalid access token |
Re-authenticate to obtain a fresh token; verify client ID and secret are correct |
|
403 Forbidden |
Access denied |
Missing Dynamics 365 security role |
Check the user or app’s security role assignment inside Dynamics 365 |
|
404 Not Found |
Resource not found |
Wrong URL, entity name, or record GUID |
Verify the entity name (plural), environment URL, and API version (v9.2) |
|
412 Precondition |
Concurrency conflict |
ETag mismatch on PATCH or DELETE |
Re-retrieve the record to get the current ETag and retry |
|
429 Too Many Req. |
Rate limit exceeded |
Too many API calls in a short window |
Implement exponential backoff and retry; check API call limits for your license |
|
500 Server Error |
Internal server error |
Issue on the Dynamics 365 platform side |
Check the error details in the response body; review the Dynamics 365 service health page |
The response body for all errors follows a consistent structure including an error code and message property. Always log the full response body, not just the status code, to diagnose issues efficiently.
Always include $select in your requests to return only the fields your application needs. Dynamics 365 entities can have hundreds of fields, and retrieving all of them unnecessarily increases response payload size, slows down queries, and consumes more API call capacity. For large-volume integrations, the difference between selecting 5 fields and returning all fields can be a 10x difference in response time.
Never assume all results fit in a single response. Use the Prefer: odata.maxpagesize header to set an explicit page size and always check for @odata.nextLink in the response. Process each page sequentially rather than attempting to retrieve all results at once. For very large data sets, consider using Dataverse batch requests or FetchXML-based paging cookies for more efficient pagination.
Client secrets must never be embedded in source code, stored in version control, or exposed in client-side JavaScript. Use Azure Key Vault to store secrets and retrieve them at runtime via managed identity. Rotate secrets before they expire using the expiration date set during creation. If a secret is compromised, revoke it immediately in the Microsoft Entra ID portal and issue a new one.
Transient failures and rate limit responses (429) are normal in production environments. Implement exponential backoff: on a 429 response, wait for the duration specified in the Retry-After response header before retrying.
For 500 errors, use an exponential backoff starting at 1 second, doubling up to a maximum of 60 seconds, with a maximum of three to five retry attempts. Log all retries for observability.
Dynamics 365 enforces service protection API limits based on the number of requests, execution time, and concurrent requests per user or application over a five-minute sliding window. Monitor your application’s API consumption through the Dynamics 365 admin center and Azure Monitor. Set up alerts if call volume approaches limit thresholds. Applications that regularly hit rate limits should be redesigned to batch operations, cache frequently read data, or distribute load across multiple service accounts.
The Microsoft Dynamics 365 Web API gives your team a standards-based, secure, and flexible interface for connecting Dynamics 365 to the rest of your technology ecosystem. Whether you’re building your first integration, automating a manual workflow, or architecting a full custom application on Dataverse, the Web API is the right foundation.
IES brings years of expertise in Microsoft Dynamics 365 solutions to help businesses unlock the full potential of their CRM and ERP environments. Contact us to learn how IES can help you integrate, extend, and get more from your Dynamics 365 investment.