Marketplace metered billing APIs
The metered billing APIs should be used when the publisher creates custom metering dimensions for an offer to be published in Partner Center. Integration with the metered billing APIs is required for any purchased offer that has one or more plans with custom dimensions to emit usage events.
Important
You must keep track of the usage in your code and only send usage events to Microsoft for the usage that is above the base fee.
For more information on creating custom metering dimensions for SaaS, see SaaS metered billing.
For more information on creating custom metering dimensions for an Azure Application offer with a Managed app plan, see Configure your Azure application offer setup details.
Enforcing TLS 1.2 Note
TLS version 1.2 version is enforced as the minimal version for HTTPS communications. Make sure you use this TLS version in your code. TLS version 1.0 and 1.1 are deprecated and connection attempts will be refused.
Metered billing single usage event
The usage event API should be called by the publisher to emit usage events against an active resource (subscribed) for the plan purchased by the specific customer. The usage event is emitted separately for each custom dimension of the plan defined by the publisher when publishing the offer.
Only one usage event can be emitted for each hour of a calendar day per resource and dimension. If more than one unit is consumed in an hour, then accumulate all the units consumed in the hour and then emit it in a single event. Usage events can only be emitted for the past 24 hours. If you emit a usage event at any time between 8:00 and 8:59:59 (and it is accepted) and send an additional event for the same day between 8:00 and 8:59:59, it will be rejected as a duplicate.
POST: https://marketplaceapi.microsoft.com/api/usageEvent?api-version=<ApiVersion>
Query parameters:
Parameter | Recommendation |
---|---|
ApiVersion |
Use 2018-08-31. |
Request headers:
Content-type | Use application/json |
---|---|
x-ms-requestid |
Unique string value for tracking the request from the client, preferably a GUID. If this value is not provided, one will be generated and provided in the response headers. |
x-ms-correlationid |
Unique string value for operation on the client. This parameter correlates all events from client operation with events on the server side. If this value isn't provided, one will be generated and provided in the response headers. |
authorization |
A unique access token that identifies the ISV that is making this API call. The format is "Bearer <access_token>" when the token value is retrieved by the publisher as explained for
|
Request body example:
{
"resourceId": <guid>, // unique identifier of the resource against which usage is emitted.
"quantity": 5.0, // how many units were consumed for the date and hour specified in effectiveStartTime, must be greater than 0 or a double integer
"dimension": "dim1", // custom dimension identifier
"effectiveStartTime": "2018-12-01T08:30:14", // time in UTC when the usage event occurred, from now and until 24 hours back
"planId": "plan1", // id of the plan purchased for the offer
}
For Azure Application Managed Apps plans, the resourceId
is the Managed App resource group Id
. An example script for fetching it can be found in using the Azure-managed identities token.
For SaaS offers, the resourceId
is the SaaS subscription ID. For more details on SaaS subscriptions, see list subscriptions.
Responses
Code: 200
OK. The usage emission was accepted and recorded on Microsoft side for further processing and billing.
Response payload example:
{
"usageEventId": <guid>, // unique identifier associated with the usage event in Microsoft records
"status": "Accepted" // this is the only value in case of single usage event
"messageTime": "2020-01-12T13:19:35.3458658Z", // time in UTC this event was accepted
"resourceId": <guid>, // unique identifier of the resource against which usage is emitted. For SaaS it's the subscriptionId.
"quantity": 5.0, // amount of emitted units as recorded by Microsoft
"dimension": "dim1", // custom dimension identifier
"effectiveStartTime": "2018-12-01T08:30:14", // time in UTC when the usage event occurred, as sent by the ISV
"planId": "plan1", // id of the plan purchased for the offer
}
Code: 400
Bad request.
- Missing or invalid request data provided.
effectiveStartTime
is more than 24 hours in the past. Event has expired.- SaaS subscription isn't in Subscribed status.
Response payload example:
{
"message": "One or more errors have occurred.",
"target": "usageEventRequest",
"details": [
{
"message": "The resourceId is required.",
"target": "ResourceId",
"code": "BadArgument"
}
],
"code": "BadArgument"
}
Code: 403
Forbidden. The authorization token isn't provided, is invalid or expired. Or the request is attempting to access a subscription for an offer that was published with a different Microsoft Entra App ID from the one used to create the authorization token.
Code: 409
Conflict. A usage event has already been successfully reported for the specified resource ID, effective usage date and hour.
Response payload example:
{
"additionalInfo": {
"acceptedMessage": {
"usageEventId": "<guid>", //unique identifier associated with the usage event in Microsoft records
"status": "Duplicate",
"messageTime": "2020-01-12T13:19:35.3458658Z",
"resourceId": "<guid>", //unique identifier of the resource against which usage is emitted.
"quantity": 1.0,
"dimension": "dim1",
"effectiveStartTime": "2020-01-12T11:03:28.14Z",
"planId": "plan1"
}
},
"message": "This usage event already exist.",
"code": "Conflict"
}
Metered billing batch usage event
The batch usage event API allows you to emit usage events for more than one purchased resource at once. It also allows you to emit several usage events for the same resource as long as they're for different calendar hours. The maximal number of events in a single batch is 25.
POST: https://marketplaceapi.microsoft.com/api/batchUsageEvent?api-version=<ApiVersion>
Query parameters:
Parameter | Recommendation |
---|---|
ApiVersion |
Use 2018-08-31. |
Request headers:
Content-type | Use application/json |
---|---|
x-ms-requestid |
Unique string value for tracking the request from the client, preferably a GUID. If this value isn't provided, one will be generated, and provided in the response headers. |
x-ms-correlationid |
Unique string value for operation on the client. This parameter correlates all events from client operation with events on the server side. If this value isn't provided, one will be generated, and provided in the response headers. |
authorization |
A unique access token that identifies the ISV that is making this API call. The format is Bearer <access_token> when the token value is retrieved by the publisher as explained for
|
Note
In the request body, the resource identifier has different meanings for SaaS app and for Azure Managed app emitting custom meter. The resource identifier for SaaS App is resourceID
. The resource identifier for Azure Application Managed Apps plans is resourceUri
. For more information on resource identifiers, see Azure Marketplace Metered Billing- Picking the correct ID when submitting usage events.
For SaaS offers, the resourceId
is the SaaS subscription ID. For more details on SaaS subscriptions, see list subscriptions.
Request body example for SaaS apps:
{
"request": [ // list of usage events for the same or different resources of the publisher
{ // first event
"resourceId": "<guid1>", // Unique identifier of the resource against which usage is emitted.
"quantity": 5.0, // how many units were consumed for the date and hour specified in effectiveStartTime, must be greater than 0 or a double integer
"dimension": "dim1", //Custom dimension identifier
"effectiveStartTime": "2018-12-01T08:30:14",//Time in UTC when the usage event occurred, from now and until 24 hours back
"planId": "plan1", // id of the plan purchased for the offer
},
{ // next event
"resourceId": "<guid2>",
"quantity": 39.0,
"dimension": "email",
"effectiveStartTime": "2018-11-01T23:33:10
"planId": "gold", // id of the plan purchased for the offer
}
]
}
For Azure Application Managed Apps plans, the resourceUri
is the Managed Application resourceUsageId
. An example script for fetching it can be found in using the Azure-managed identities token.
Request body example for Azure Application managed apps:
{
"request": [ // list of usage events for the same or different resources of the publisher
{ // first event
"resourceUri": "<fullyqualifiedname>", // Unique identifier of the resource against which usage is emitted.
"quantity": 5.0, // how many units were consumed for the date and hour specified in effectiveStartTime, must be greater than 0 or a double integer
"dimension": "dim1", //Custom dimension identifier
"effectiveStartTime": "2018-12-01T08:30:14",//Time in UTC when the usage event occurred, from now and until 24 hours back
"planId": "plan1", // id of the plan purchased for the offer
}
]
}
Responses
Code: 200
OK. The batch usage emission was accepted and recorded on Microsoft side for further processing and billing. The response list is returned with status for each individual event in the batch. You should iterate through the response payload to understand the responses for each individual usage event sent as part of the batch event.
Response payload example:
{
"count": 2, // number of records in the response
"result": [
{ // first response
"usageEventId": "<guid>", // unique identifier associated with the usage event in Microsoft records
"status": "Accepted" // see list of possible statuses below,
"messageTime": "2020-01-12T13:19:35.3458658Z", // Time in UTC this event was accepted by Microsoft,
"resourceId": "<guid1>", // unique identifier of the resource against which usage is emitted.
"quantity": 5.0, // amount of emitted units as recorded by Microsoft
"dimension": "dim1", // custom dimension identifier
"effectiveStartTime": "2018-12-01T08:30:14",// time in UTC when the usage event occurred, as sent by the ISV
"planId": "plan1", // id of the plan purchased for the offer
},
{ // second response
"status": "Duplicate",
"messageTime": "0001-01-01T00:00:00",
"error": {
"additionalInfo": {
"acceptedMessage": {
"usageEventId": "<guid>",
"status": "Duplicate",
"messageTime": "2020-01-12T13:19:35.3458658Z",
"resourceId": "<guid2>",
"quantity": 1.0,
"dimension": "email",
"effectiveStartTime": "2020-01-12T11:03:28.14Z",
"planId": "gold"
}
},
"message": "This usage event already exist.",
"code": "Conflict"
},
"resourceId": "<guid2>",
"quantity": 1.0,
"dimension": "email",
"effectiveStartTime": "2020-01-12T11:03:28.14Z",
"planId": "gold"
}
]
}
Description of status code referenced in BatchUsageEvent
API response:
Status code | Description |
---|---|
Accepted |
Accepted. |
Expired |
Expired usage. |
Duplicate |
Duplicate usage provided. |
Error |
Error code. |
ResourceNotFound |
The usage resource provided is invalid. |
ResourceNotAuthorized |
You aren't authorized to provide usage for this resource. |
ResourceNotActive |
The resource is suspended or was never activated. |
InvalidDimension |
The dimension for which the usage is passed is invalid for this offer/plan. |
InvalidQuantity |
The quantity passed is lower or equal to 0. |
BadArgument |
The input is missing or malformed. |
Code: 400
Bad request. The batch contained more than 25 usage events.
Code: 403
Forbidden. The authorization token isn't provided, is invalid or expired. Or the request is attempting to access a subscription for an offer that was published with a different Microsoft Entra App ID from the one used to create the authorization token.
Metered billing retrieve usage events
You can call the usage events API to get the list of usage events. ISVs can use this API to see the usage events that have been posted for a certain configurable duration of time and what state these events are at the point of calling the API.
GET: https://marketplaceapi.microsoft.com/api/usageEvents
Query parameters:
Parameter | Recommendation |
---|---|
ApiVersion | Use 2018-08-31. |
usageStartDate | DateTime in ISO8601 format. For example, 2020-12-03T15:00 or 2020-12-03 |
UsageEndDate (optional) | DateTime in ISO8601 format. Default = current date |
offerId (optional) | Default = all available |
planId (optional) | Default = all available |
dimension (optional) | Default = all available |
azureSubscriptionId (optional) | Default = all available |
reconStatus (optional) | Default = all available |
Possible values of reconStatus:
ReconStatus | Description |
---|---|
Submitted | Not yet processed by PC Analytics |
Accepted | Matched with PC Analytics |
Rejected | Rejected in the pipeline. Contact Microsoft support to investigate the cause. |
Mismatch | MarketplaceAPI and Partner Center Analytics quantities are both non-zero, however not matching |
Request headers:
Content type | Use application/json |
---|---|
x-ms-requestid | Unique string value (preferably a GUID), for tracking the request from the client. If this value isn't provided, one will be generated and provided in the response headers. |
x-ms-correlationid | Unique string value for operation on the client. This parameter correlates all events from client operation with events on the server side. If this value isn't provided, one will be generated and provided in the response headers. |
authorization | A unique access token that identifies the ISV that is making this API call. The format is Bearer <access_token> when the token value is retrieved by the publisher. For more information, see:
|
Responses
Response payload examples:
Accepted*
[
{
"usageDate": "2020-11-30T00:00:00Z",
"usageResourceId": "11111111-2222-3333-4444-555555555555",
"dimension": "tokens",
"planId": "silver",
"planName": "Silver",
"offerId": "mycooloffer",
"offerName": "My Cool Offer",
"offerType": "SaaS",
"azureSubscriptionId": "12345678-9012-3456-7890-123456789012",
"reconStatus": "Accepted",
"submittedQuantity": 17.0,
"processedQuantity": 17.0,
"submittedCount": 17
}
]
Submitted
[
{
"usageDate": "2020-11-30T00:00:00Z",
"usageResourceId": "11111111-2222-3333-4444-555555555555",
"dimension": "tokens",
"planId": "silver",
"planName": "",
"offerId": "mycooloffer",
"offerName": "",
"offerType": "SaaS",
"azureSubscriptionId": "12345678-9012-3456-7890-123456789012",
"reconStatus": "Submitted",
"submittedQuantity": 17.0,
"processedQuantity": 0.0,
"submittedCount": 17
}
]
Mismatch
[
{
"usageDate": "2020-11-30T00:00:00Z",
"usageResourceId": "11111111-2222-3333-4444-555555555555",
"dimension": "tokens",
"planId": "silver",
"planName": "Silver",
"offerId": "mycooloffer",
"offerName": "My Cool Offer",
"offerType": "SaaS",
"azureSubscriptionId": "12345678-9012-3456-7890-123456789012",
"reconStatus": "Mismatch",
"submittedQuantity": 17.0,
"processedQuantity": 16.0,
"submittedCount": 17
}
]
Rejected
[
{
"usageDate": "2020-11-30T00:00:00Z",
"usageResourceId": "11111111-2222-3333-4444-555555555555",
"dimension": "tokens",
"planId": "silver",
"planName": "",
"offerId": "mycooloffer",
"offerName": "",
"offerType": "SaaS",
"azureSubscriptionId": "12345678-9012-3456-7890-123456789012",
"reconStatus": "Rejected",
"submittedQuantity": 17.0,
"processedQuantity": 0.0,
"submittedCount": 17
}
]
Status codes
Code: 403 Forbidden. The authorization token isn't provided, is invalid or expired. Or the request is attempting to access a subscription for an offer that was published with a different Microsoft Entra App ID from the one used to create the authorization token.
Development and testing best practices
To test the custom meter emission, implement the integration with metering API, create a plan for your published SaaS offer with custom dimensions defined in it with zero price per unit. And publish this offer as preview so only limited users would be able to access and test the integration.
You can also use private plan for an existing live offer to limit the access to this plan during testing to limited audience.
Get support
Follow the instruction in Support for the commercial marketplace program in Partner Center to understand publisher support options and open a support ticket with Microsoft.
Related content
For more information on metering service APIs, see Marketplace metering service APIs FAQ.