Exporting API Consumer Billing Data
Export usage data to bill API consumers using your enterprise's billing platform.
Enterprise Hub customers only
This document does not apply to rapidapi.com users.
Overview
For Enterprise Hub customers who want to monetize their APIs while managing invoicing and payment collections outside of the Enterprise Hub, Rapid can enable the option to export quota usage reports and subscription details. The exported files are in CSV format. This allows your enterprise to parse and feed the required information into your existing billing platforms to collect payments from consumers.
The following files are related to exporting usage data:
- quotas.csv - An export of the details about quota usage of different subscriptions.
- subscriptions.csv - An export of the subscriptions that need to be invoiced for a given timeframe.
Exporting the data
The quota and subscription information should be retrieved using the GraphQL Platform API, from the personal context of an Environment Admin. Obtaining the quotas.csv
and/or subscriptions.csv
files is a two-step process:
- The
generateFile
mutation is used to create the latestquotas.csv
and/orsubscriptions.csv
files. - The
getFileDetails
query is used to download the files.
1. Generating the CSV files
Use the generateFile
mutation to generate the quota and/or subscription files. In the Variables, set the actionName
to either SUBSCRIPTIONS
or QUOTAS
. The from
and to
dates can be provided in ISO 8601 format (as shown below) or in UNIX time. The date range can not be larger than 31 days. Data is only available for the most recent 90 days.
The returned action_status
value can be PENDING
, IN_PROGRESS
, SUCCESS
, or FAILED
. You can download the file once the status is SUCCESS
. The returned id
is needed to download the file.
mutation generateFile($input: generateFileInput!) {
generateFile(input: $input) {
id
action_name
action_status
result_file_link
}
}
{
"input": {
"from": "2023-09-01T00:00:00.000Z",
"to": "2023-09-30T23:59:59.999Z",
"actionName": "SUBSCRIPTIONS"
}
}
Limitations for the
generateFile
mutation
- The date range can not be larger than 31 days.
- Data is only available for the most recent 90 days.
- There will be a limitation of 10 successful calls to
generateFile
per calendar month. When the limit is exceeded, the request will be denied with a clear error.- Once a file is generated, it is available to be downloaded for a period of 5 days.
- The
SUBSCRIPTIONS
export will only contain the following subscriptions:
- All active subscriptions in the timeframe (created during or prior to the timeframe).
- Subscriptions that were deleted in the timeframe.
2. Downloading the CSV files
Once a file has been generated (see above) and has returned an action_status
of SUCCESS
, you can use the getFileDetails
query to download it. In the Variables, you must pass the id
returned from the generateFile
mutation.
query getFileDetails($id: ID!) {
getFileDetails(id: $id) {
id
action_name
action_status
result_file_link
}
}
{
"id": "id_that_was_provided_in_previous_mutation_a16a5e74-35e3-4ed8..."
}
Sample: Constructing an invoice from the data
The following is a Node.js program that suggests how to combine CSV files above into JSON formatted data. For each user or team, the JSON lists the subscriptions, price, API name, and any overages.
Note: This is only a suggested implementation that can be further tweaked and customized to your specific needs.
const fs = require('fs');
const csv = require("csvtojson");
const calculateOverCharge = (
subscription,
quotasBySubscriptionId
) => {
const {
subscription_id,
object_id,
object_limit_type,
object_name,
object_overage_cost,
object_quota_limit,
object_quota_type,
} = subscription;
const currentObjectQuotas = quotasBySubscriptionId[subscription_id]?.[object_id];
if (!currentObjectQuotas) {
return
}
// if overCharge is zero there is no sense to calculate it
if (Number(object_overage_cost) === 0) {
return;
}
let over_usage_amount = 0;
let over_usage_total_price = 0;
if (object_quota_type === 'MONTHLY') {
const sumOfAllQuotas = currentObjectQuotas.reduce((result, currentQuota) => {
return result + Number(currentQuota.object_usage)
}, 0);
const delta = sumOfAllQuotas - Number(object_quota_limit)
over_usage_amount = delta > 0 ? delta : 0;
over_usage_total_price = over_usage_amount * Number(object_overage_cost);
} else {
const sumOfAllQuotas = currentObjectQuotas.reduce((result, currentQuota) => {
const delta = Number(currentQuota.object_usage) - Number(object_quota_limit);
return delta > 0 ? result + delta : result
}, 0);
over_usage_amount = sumOfAllQuotas;
over_usage_total_price = over_usage_amount * Number(object_overage_cost);
}
// there is no over usage
if (over_usage_amount === 0) {
return;
}
return {
object_id,
object_limit_type,
object_name,
object_overage_cost: +object_overage_cost,
object_quota_limit: +object_quota_limit,
object_quota_type,
over_usage_total_price,
over_usage_amount,
}
}
const run = async () => {
const quotas = await csv().fromFile('./quotas.csv');
const quotasBySubscriptionId = quotas.reduce((result, quota) => {
const currentSubscription = result[quota.subscription_id];
if (!quota.object_id) {
return result;
}
return {
...result,
[quota.subscription_id]: {
...currentSubscription,
[quota.object_id]: [...(currentSubscription?.[quota.object_id] || []), quota],
}
}
}, {});
const subscriptions = await csv().fromFile('./subscriptions.csv');
const subscriptionsByConsumerIDs = {};
subscriptions.forEach(subscription => {
const subscriptionConsumerID = subscription.subscription_owner_id;
const subscriptionID = subscription.subscription_id;
// consumer not yet populated in the subscriptionsByConsumerIDs object
if (!subscriptionsByConsumerIDs[subscriptionConsumerID]) {
const {
// Subscription data
subscription_owner_id,
subscription_owner_org_email,
subscription_owner_org_id,
subscription_owner_org_name,
subscription_owner_team_name,
subscription_owner_type,
subscription_owner_user_email,
subscription_owner_user_name,
} = subscription;
subscriptionsByConsumerIDs[subscriptionConsumerID] = {
consumerData: {
subscription_owner_id,
subscription_owner_org_email,
subscription_owner_org_id,
subscription_owner_org_name,
subscription_owner_team_name,
subscription_owner_type,
subscription_owner_user_email,
subscription_owner_user_name,
},
subscriptions: {},
}
}
// the current subscription not yet populated in the subscriptions object
if (!subscriptionsByConsumerIDs[subscriptionConsumerID].subscriptions[subscriptionID]) {
const {
// API data
api_id,
api_name,
api_owner_id,
api_owner_org_email,
api_owner_org_id,
api_owner_org_name,
api_owner_team_name,
api_owner_type,
api_owner_user_email,
api_owner_user_name,
api_version_id,
api_version_name,
// Plan details
api_billing_plan_name,
api_billing_plan_plan_type,
api_billing_plan_price,
api_billing_plan_version_id,
//subscription data
subscription_created_date,
subscription_deleted_date,
} = subscription;
subscriptionsByConsumerIDs[subscriptionConsumerID].subscriptions[subscriptionID] = {
created_date: subscription_created_date,
deleted_date: subscription_deleted_date,
plan: {
api_billing_plan_name,
api_billing_plan_plan_type,
api_billing_plan_price: api_billing_plan_price ? Number(api_billing_plan_price) : 0,
api_billing_plan_version_id,
},
api: {
api_id,
api_name,
api_owner_id,
api_owner_org_email,
api_owner_org_id,
api_owner_org_name,
api_owner_team_name,
api_owner_type,
api_owner_user_email,
api_owner_user_name,
api_version_id,
api_version_name,
},
overUsage: []
}
}
const calculatedOverCharge = calculateOverCharge(
subscription,
quotasBySubscriptionId
)
if (calculatedOverCharge) {
subscriptionsByConsumerIDs[subscriptionConsumerID].subscriptions[subscriptionID].overUsage.push(calculatedOverCharge)
}
})
return subscriptionsByConsumerIDs;
}
Suggested usage
- The API builder publishes APIs that include paid plans.
- The API will have a Free plan that has a strict quota, allowing Consumers to immediately gain access to the API for testing purposes.
- The paid plans have the "Request Approval" option enabled.
- The API consumer asks to subscribe to the API.
- The API builder and potential consumer agree on a payment method and sign an agreement.
- The API builder approves the subscription.
- The API consumer uses the API.
- On a monthly basis, an environment admin collects the quota data for the Enterprise Hub and processes it. (Or this process is automated.)
- Each API builder receives usage information for each of his subscribers and invoices the consumers directly.
Fields of the subscriptions CSV file
Section | Column Name | Enum Values | Description / Notes |
---|---|---|---|
API owner details | api_owner_id | Unique id of the entity that owns the API | |
api_owner_type | User, Team | ||
api_owner_user_name | Empty if owner is a team | ||
api_owner_user_email | Empty if owner is a team | ||
api_owner_team_name | Empty if owner is a user | ||
api_owner_org_id | Empty if owner is a user | ||
api_owner_org_name | Empty if owner is a user | ||
api_owner_org_email | Empty if owner is a user | ||
API details | api_id | ||
api_name | |||
api_version_id | |||
api_version_name | |||
subscription id | subscription_id | ||
user details | subscription_owner_id | Unique id of the entity that owns the subscription | |
subscription_owner_type | User, Team | ||
subscription_owner_user_name | Empty if owner is a team | ||
subscription_owner_user_email | Empty if owner is a team | ||
subscription_owner_team_name | Empty if owner is a user | ||
subscription_owner_org_id | Empty if owner is a user | ||
subscription_owner_org_name | Empty if owner is a user | ||
subscription_owner_org_email | Empty if owner is a user | ||
subscription pricing and usage | api_billing_plan_name | BASIC, PRO, CUSTOM-plan, etc. | |
api_billing_plan_price | |||
api_billing_plan_type | MONTHLY, PERUSE | ||
api_billing_plan_version_id | Value such as billingplanversion_82b7ab3f-c67c-4886-a0fd-168b9dd822xx | ||
subscription_status | ACTIVE, DELETED | Stat of the subscription at the time the exporting process took place | |
subscription_parent_id | If the subscription is for a team, this value contains the organization ID, such as 5755575 | ||
subscription_created_date | Value in ISO 8601 format | ||
subscription_cancelled_date | |||
subscription_deleted_date | |||
object_id | Value such as billingitem_6c62eea2-a22c-4b98-a926-e3d4ef0a84xx | ||
object_name | Billing object name such as Requests or Email | ||
object_limit_type | soft, hard | Empty if limit_period = UNLIMITED | |
object_overage_cost | Price per billing item beyond the agreed limit. 0 means unlimited. Applies only for soft limits. | ||
object_quota_limit | Integer 0 if object_quota_type = UNLIMITED | Billing object quota limit value (e.g. max number of requests per day - not Rate Limit). 0 if object_quota_type = UNLIMITED | |
object_quota_type | UNLIMITED, MONTHLY, DAILY | Billing object quota limit period |
Fields of the quotas CSV file
Section | Column Name | Enum Values | Description / Notes |
---|---|---|---|
API details | api_id | ||
api_name | |||
api_version_id | |||
api_version_name | |||
subscription id | subscription_id | ||
subscription pricing and usage | api_billing_plan_name | BASIC, PRO, CUSTOM-plan, etc. | |
api_billing_plan_version_id | Value such as billingplanversion_82b7ab3f-c67c-4886-a0fd-168b9dd822xx | ||
object_id | Value such as billingitem_6c62eea2-a22c-4b98-a926-e3d4ef0a84xx | ||
object_name | Billing object name such as Requests or Email | ||
date | The date when the API consumer made the call to the API | ||
object_usage | The number of calls made to a specific object (requests, calls, tokens, coins, etc.). Each api call may trigger multiple objects. |
Notes
- Additional consideration is needed to allow using this process in conjunction with API plans that include hard limits.
Contact your Rapid representative for additional information.
Updated about 1 year ago