API throttling guidance for partners calling Partner Center APIs
Microsoft is implementing API throttling to allow more consistent performance within a time span for partners calling the Partner Center APIs. Throttling limits the number of requests to a service in a time span to prevent overuse of resources. While Partner Center is designed to handle a high volume of requests, if an overwhelming number of requests occur by few partners, throttling helps maintain optimal performance and reliability for all partners.
Throttling limits vary based on the scenario. For example, if you're performing a large volume of writes, the possibility for throttling is higher than if you're only performing reads.
What happens when throttling occurs?
When a throttling threshold is exceeded, Partner Center limits any further requests from that client for a period of time. Throttling behavior depends on the type and number of requests.
Common throttling scenarios
The most common causes of throttling of clients include:
- A large number of requests for an API per Partner Tenant ID: for some Partner Center APIs, throttling is determined by Partner Tenant ID. Too many calls to those APIs on the same Partner Tenant ID results in exceeding the throttling threshold.
- A large number of requests for an API per Partner Tenant ID per Customer Tenant ID: for other APIs, throttling is determined by Partner Tenant ID/Customer Tenant ID combination; in those cases, making too many calls against the same customer tenant ID results in throttling - while calls against other customers might succeed.
Best practices to avoid throttling
Programming practices, such as continuously polling a resource to check for updates and regularly scanning resource collections to check for new or deleted resources, are more likely to lead to throttling and degrades overall performance. Concurrent API calls might lead to high number of requests per unit time, which causes requests to be throttled. You should instead use change tracking and change notifications. Additionally, you should be able to use activity logs for detecting changes. For more information, see Partner Center activity logs. We highly recommend partners to consider using the activity log API for more efficiency and to avoid throttling. See also the example of using activity logs, below.
Best practices to handle throttling
The following are best practices for handling throttling:
- Reduce the degree of parallelism.
- Reduce the frequency of calls.
- Avoid immediate retries because all requests accrue against your usage limits.
When you implement error handling, use the HTTP error code 429 to detect throttling. The failed response includes the Retry-After response header. Backing off requests using the Retry-after delay is the fastest way to recover from throttling.
To use the Retry-after delay:
- Wait the number of seconds specified in the Retry-After header.
- Retry the request.
- If the request fails again with a 429 error code, you're still being throttled. Retry with Exponential backoff, use the recommended Retry-After delay and retry the request until it succeeds.
- If you're using the SDK, you receive an exception with status code 429 when your request is being throttled. Use the RetryAfter property in the exception and retry the request after the time is elapsed.
APIs currently affected by throttling
In the end, every single Partner Center API that calls the endpoint "api.partnercenter.microsoft.com/" is throttled. Currently, the throttling limits are only enforced on the APIs listed below. Partner Center is collecting the telemetry on each of the APIs and dynamically adjusts the throttling limits. The following table lists the APIs where throttling is currently enforced.
Operation | Partner Center documentation |
---|---|
{baseURL}/v1/customers/{customer_id}/orders | create an order |
{baseURL}/v1/customers/{customer-tenant-id}/subscriptions/{id-for-subscription}/upgrades | transition a subscription |
{baseURL}/v1/customers/{customer-tenant-id}/orders/{order-id} | purchase an addon to a subscription |
{baseURL}/v1/customers/{customer-id}/carts/{cart-id} | create a cart |
{baseURL}/v1/customers/{customer-id}/carts/{cart-id}/checkout | checkout a cart |
{baseURL}/v1/customers/{customer-id}/carts/{cart-id} | update a cart |
{baseURL}/v1/customers/{customer-id}/subscriptions/{subscription-id}/registrations | register a subscription |
{baseURL}/v1/productupgrades | create product upgrade entity |
{baseURL}/v1/customers/{customer-id}/subscriptions/{subscription-id}/conversions | convert a trial subscription to paid |
{baseURL}/v1/customers/{customer-tenant-id} | get a customer by ID |
{baseURL}/v1/customers/{customer-tenant-id} | delete a customer account from Sandbox |
{baseURL}/v1/customers?size={size} | get a list of customers |
{baseURL}/v1/productUpgrades/eligibility | get eligibility for product upgrade |
{baseURL}/v1/customers/{customer-tenant-id}/subscriptions/{id-for-subscription} | manage subscription |
{baseURL}/v1/customers/{customer_id}/subscriptions | get-all-of-a-customer-s-subscriptions |
{baseURL}/v1/customers/{customer_id}/subscriptions/{subscription_id} | Get a subscription by ID |
{baseURL}/v1/customers/{customer_id}/orders | Get all customer orders |
{baseURL}/v1/customers/{customer_id}/orders/{order_id} | Get an order by ID |
{baseURL}/v1/customers/{customer_id}/orders/{order_id}/provisioningstatus | Get subscription provisioning status |
{baseURL}/v1/customers/{customer_id}/subscriptions/{subscription_id} | Manage orders and manage a subscription |
{baseURL}/v1/customers/{customer_id}/subscriptions/{subscription_id}/addons | Get a list of add-ons for a subscription |
{baseURL}/v1/customers/{customer_id}/subscriptions/{subscription_id}/azureEntitlements | Get a list of Azure entitlements for a subscription |
{baseURL}/v1/customers/{customer_id}/subscriptions/{subscription_id}/registrationstatus | Get subscription registration status |
{baseURL}/v1/customers/{customer-tenant-id}/transfers | Get all of a customer's transfers |
{baseURL}/v1/productUpgrades/{upgrade-id}/status | Get product upgrade status |
{baseURL}/v1/customers/{customer-id}/subscriptions/{subscription-id}/conversions | Get a list of trial conversion offers |
{baseURL}/v1/customers/{customer-tenant-id}/migrations/newcommerce/validate | Validate a subscription for migration |
{baseURL}/v1/customers/{customer-tenant-id}/migrations/newcommerce | Create a new commerce migration |
{baseURL}/v1/customers/{customerId}/promotionEligibilities | Verify a promotion eligibility |
Error code response:
HTTP/1.1 429 Too Many Requests
Content-Length: 84
Content-Type: application/json
Retry-After: 57
Date: Tue, 21 Jul 2020 04:10:58 GMT
{ "statusCode": 429, "message": "Rate limit is exceeded. Try again in 57 seconds." }
Example of activity log
For best practice in analyzing daily changes, we recommend that you query audit record for a specific day.
In the response, you get a result with changes to specific operation type. You can filter based on the operation you care about. For example, if you're interested in a newly created customer, you can look at operationType = "add_customer".
List of operationtype/resources can be found in these API docs.
Response example
Request:
Http Get call: https://api.partnercenter.microsoft.com/v1/auditrecords?startDate=2020-09-02&endDate=2020-09-02&size=50
Authorization: Bearer <token>
Accept: application/json
MS-RequestId: 127facaa-e389-41f8-8bb7-1d1af99db893
MS-CorrelationId: aaaa0000-bb11-2222-33cc-444444dddddd
X-Locale: en-US
Host: api.partnercenter.microsoft.com
Connection: Keep-Alive
Response:
{
"totalCount": 17,
"items": [
{
"id": "9daaeb1c-4195-4db5-9f1d-509eb70c8c2d_e905b566-4779-4e57-944c-7b1b5312705b_updatecustomeruserlicenses_637346859797753934",
"partnerId": "9daaeb1c-4195-4db5-9f1d-509eb70c8c2d",
"participants": [
"9daaeb1c-4195-4db5-9f1d-509eb70c8c2d"
],
"customerId": "e905b566-4779-4e57-944c-7b1b5312705b",
"userPrincipalName": "admin@testsw09.onmicrosoft.com",
"applicationId": "FulfillmentService",
"resourceType": "license",
"operationType": "update_customer_user_licenses",
"operationDate": "2020-09-02T23:26:19.7753934Z",
"operationStatus": "succeeded",
"customizedData": [
{
"key": "CustomerUserId",
"value": "933808c7-b165-496c-a24e-1a4b7846fab4"
}
],
"attributes": {
"objectType": "AuditRecord"
}
},
{
"id": "9daaeb1c-4195-4db5-9f1d-509eb70c8c2d_86bddccf-9a53-40c6-907c-08067a3f8da7_ia80zlkxp6ewoqpp35pbqjlhqv9iigvz1_createorder_637346662909268372",
"partnerId": "9daaeb1c-4195-4db5-9f1d-509eb70c8c2d",
"participants": [
"9daaeb1c-4195-4db5-9f1d-509eb70c8c2d"
],
"customerId": "aaaabbbb-0000-cccc-1111-dddd2222eeee",
"customerName": "CustomMetersStagingTest",
"userPrincipalName": "admin@testsw09.onmicrosoft.com",
"applicationId": "00001111-aaaa-2222-bbbb-3333cccc4444",
"resourceType": "order",
"resourceNewValue": "{\"Id\":\"Ia80ZLkXp6eWOqpp35pBQJLhqv9IiGVZ1\",\"AlternateId\":\"64144d300bde\",\"ReferenceCustomerId\":\"aaaabbbb-0000-cccc-1111-dddd2222eeee\",\"BillingCycle\":\"monthly\",\"CurrencyCode\":\"USD\",\"CurrencySymbol\":\"$\",\"LineItems\":[{\"LineItemNumber\":0,\"ProvisioningContext\":null,\"OfferId\":\"DZH318Z0C964:0001:DZH318Z0BZDG\",\"SubscriptionId\":\"aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e\",\"ParentSubscriptionId\":null,\"TermDuration\":\"P1M\",\"TransactionType\":\"New\",\"FriendlyName\":\"SaaS custom meter offer - Bronze\",\"Quantity\":1,\"Pricing\":null,\"PartnerIdOnRecord\":null,\"RenewsTo\":null,\"Links\":{\"Product\":{\"Uri\":\"/products/DZH318Z0C964?country=US\",\"Method\":\"GET\",\"Body\":null,\"Headers\":[]},\"Sku\":{\"Uri\":\"/products/DZH318Z0C964/skus/0001?country=US\",\"Method\":\"GET\",\"Body\":null,\"Headers\":[]},\"Availability\":{\"Uri\":\"/products/DZH318Z0C964/skus/0001/availabilities/DZH318Z0BZDG?country=US\",\"Method\":\"GET\",\"Body\":null,\"Headers\":[]},\"ActivationLinks\":{\"Uri\":\"/customers/aaaabbbb-0000-cccc-1111-dddd2222eeee/orders/Ia80ZLkXp6eWOqpp35pBQJLhqv9IiGVZ1/lineitems/0/activationlinks\",\"Method\":\"GET\",\"Body\":null,\"Headers\":[]}}}],\"CreationDate\":\"2020-09-02T17:58:01.7755853Z\",\"Status\":\"pending\",\"TransactionType\":\"UserPurchase\",\"Links\":{\"Self\":{\"Uri\":\"/customers/aaaabbbb-0000-cccc-1111-dddd2222eeee/orders/Ia80ZLkXp6eWOqpp35pBQJLhqv9IiGVZ1\",\"Method\":\"GET\",\"Body\":null,\"Headers\":[]},\"ProvisioningStatus\":{\"Uri\":\"/customers/aaaabbbb-0000-cccc-1111-dddd2222eeee/orders/Ia80ZLkXp6eWOqpp35pBQJLhqv9IiGVZ1/provisioningstatus\",\"Method\":\"GET\",\"Body\":null,\"Headers\":[]},\"PatchOperation\":{\"Uri\":\"/customers/aaaabbbb-0000-cccc-1111-dddd2222eeee/orders/Ia80ZLkXp6eWOqpp35pBQJLhqv9IiGVZ1\",\"Method\":\"PATCH\",\"Body\":null,\"Headers\":[]}},\"Client\":{\"marketplaceCountry\":\"US\",\"deviceFamily\":\"UniversalStore-PartnerCenter\",\"name\":\"Partner Center Web\"},\"Attributes\":{\"ObjectType\":\"Order\"}}",
"operationType": "create_order",
"originalCorrelationId": "aaaabbbb-0000-cccc-1111-dddd2222eeee",
"operationDate": "2020-09-02T17:58:10.9268372Z",
"operationStatus": "succeeded",
"customizedData": [
{
"key": "OrderId",
"value": "Ia80ZLkXp6eWOqpp35pBQJLhqv9IiGVZ1"
},
{
"key": "AlternateId",
"value": "64144d300bde"
},
{
"key": "BillingCycle",
"value": "Monthly"
},
{
"key": "OfferId-0",
"value": "DZH318Z0C964:0001:DZH318Z0BZDG"
},
{
"key": "SubscriptionId-0",
"value": "aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e"
},
{
"key": "SubscriptionName-0",
"value": "SaaS custom meter offer - Bronze"
},
{
"key": "Quantity-0",
"value": "1"
},
{
"key": "PartnerOnRecord-0",
"value": null
}
],
"attributes": {
"objectType": "AuditRecord"
}
},
{
"id": "9daaeb1c-4195-4db5-9f1d-509eb70c8c2d_86bddccf-9a53-40c6-907c-08067a3f8da7_86bddccf-9a53-40c6-907c-08067a3f8da7_addcustomer_637346648528069005",
"partnerId": "9daaeb1c-4195-4db5-9f1d-509eb70c8c2d",
"participants": [
"9daaeb1c-4195-4db5-9f1d-509eb70c8c2d"
],
"customerId": "aaaabbbb-0000-cccc-1111-dddd2222eeee",
"customerName": "CustomMetersStagingTest",
"userPrincipalName": "admin@testsw09.onmicrosoft.com",
"applicationId": "00001111-aaaa-2222-bbbb-3333cccc4444",
"resourceType": "customer",
"resourceNewValue": "{\"Id\":\"aaaabbbb-0000-cccc-1111-dddd2222eeee\",\"CommerceId\":\"9dd78b4f-f98a-44b4-a2fa-2b82ac58d24c\",\"CompanyProfile\":{\"TenantId\":\"aaaabbbb-0000-cccc-1111-dddd2222eeee\",\"Domain\":\"CustomMetersStagingTest.onmicrosoft.com\",\"CompanyName\":\"CustomMetersStagingTest\",\"Address\":null,\"Email\":null,\"OrganizationRegistrationNumber\":null,\"Links\":{\"Self\":{\"Uri\":\"/customers/aaaabbbb-0000-cccc-1111-dddd2222eeee/profiles/company\",\"Method\":\"GET\",\"Body\":null,\"Headers\":[]}},\"Attributes\":{\"ObjectType\":\"CustomerCompanyProfile\"}},\"BillingProfile\":{\"Id\":\"bbbbcccc-1111-dddd-2222-eeee3333ffff\",\"FirstName\":\"CustomMetersStagingTest\",\"LastName\":\"CustomMetersStagingTest\",\"Email\":\"CustomMetersStagingTest@CustomMetersStagingTest.com\",\"Culture\":\"en-US\",\"Language\":\"en\",\"CompanyName\":\"CustomMetersStagingTest\",\"DefaultAddress\":{\"Id\":null,\"Country\":\"US\",\"Region\":null,\"City\":\"Seattle\",\"State\":\"WA\",\"District\":null,\"AddressLine1\":\"CustomMetersStagingTest\",\"AddressLine2\":null,\"AddressLine3\":null,\"PostalCode\":\"98122\",\"FirstName\":\"CustomMetersStagingTest\",\"LastName\":\"CustomMetersStagingTest\",\"EmailAddress\":null,\"PhoneNumber\":null,\"MiddleName\":null},\"Attributes\":{\"Etag\":\"-2279334701316321663\",\"ObjectType\":\"CustomerBillingProfile\"}},\"RelationshipToPartner\":\"reseller\",\"AllowDelegatedAccess\":true,\"UserCredentials\":{\"userName\":\"admin\",\"password\":\"\"},\"AssociatedPartnerId\":null,\"CustomDomains\":null,\"Attributes\":{\"ObjectType\":\"Customer\"}}",
"operationType": "add_customer",
"originalCorrelationId": "aaaabbbb-0000-cccc-1111-dddd2222eeee",
"operationDate": "2020-09-02T17:34:12.8069005Z",
"operationStatus": "succeeded",
"customizedData": [
{
"key": "PrimaryDomainName",
"value": "CustomMetersStagingTest.onmicrosoft.com"
},
{
"key": "Relationship",
"value": "Reseller"
}
],
"attributes": {
"objectType": "AuditRecord"
}
},
...
],
"links": {
"self": {
"uri": "/auditrecords?startDate=2020-09-02&endDate=2020-09-02&size=50",
"method": "GET",
"headers": []
}
},
"attributes": {
"objectType": "Collection"
}
}