List sales through your app
Your integration dashboard’s Transactions tab: every sale through your app’s links across all your accounts, newest first, with the commission booked on each.
/api/external/crm/transactionsNewYour integration dashboard’s Transactions tab: every sale through your app’s links across all your accounts, newest first, with the commission booked on each.
Drop sales, tips and subscription payments are all included. Refunded and charged-back sales are included by default with their status (like the dashboard); pass includeReversed=false to leave them out. Window by exact instants (since / until) or by calendar days (startDate / endDate in tz), not both. Page with cursor.
Authentication
Send the creator's API key as a bearer token: Authorization: Bearer dpfn_…. See Authentication & API keys.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
since | string | Optional | Only sales paid at or after this instant (ISO 8601). Format: date-time |
until | string | Optional | Only sales paid at or before this instant (ISO 8601). Format: date-time |
startDate | string | Optional | Instead of since/until: first day, YYYY-MM-DD, in tz. Needs endDate.Format: date |
endDate | string | Optional | Last day, YYYY-MM-DD, in tz (at most 366 days).Format: date |
tz | string | Optional | IANA time zone the calendar days (and buckets) are cut in. Defaults to Europe/Stockholm, like your dashboard. Default: "Europe/Stockholm"Format: iana-timezone |
type | string | Optional | Comma-separated subset of drop,tip,subscription. Default: all. |
includeReversed | boolean | Optional | false leaves refunded and charged-back sales out.Default: true |
limit | integer | Optional | Page size (clamped to the range). Default: 50Min: 1 Max: 100 |
cursor | string | Optional | nextCursor from the previous page, unchanged. Keep the other parameters the same. |
Responses
| Name | Type | Description | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
currency | string | Currency of every *Cents field. One of: USD | ||||||||||||||||||||||||||||||||||||
transactions | CrmTransaction[] | Newest first.Show child attributes
| ||||||||||||||||||||||||||||||||||||
nextCursor | string | null | Pass as cursor for the next page; null on the last page. |
{
"currency": "USD",
"transactions": [
{
"id": "clxor0001ord",
"type": "drop",
"paidAt": "2026-10-29T18:42:11.000Z",
"transactionId": "cb_8812734",
"linkId": "k3v9x2m1qa",
"amountCents": 1500,
"crmEarnedCents": 75,
"status": "paid",
"creator": {
"id": "clxac0001acct",
"username": "valeria",
"name": "Valeria"
}
}
],
"nextCursor": "MjAyNi0xMC0yOVQxODo0MjoxMS4wMDBafGNseG9yMDAwMW9yZA"
}Errors
| Status | Body | When |
|---|---|---|
| 400 | {"error":"Use since/until or startDate/endDate, not both.","code":"invalid_input","field":"since"} | Both windows were sent. |
| 400 | {"error":"since must be an ISO 8601 date or date-time.","code":"invalid_input","field":"since"} | Unparseable `since` / `until`, or `until` before `since`. |
| 400 | {"error":"startDate must be a real calendar day, YYYY-MM-DD.","code":"invalid_input","field":"startDate"} | `startDate` / `endDate` missing (stats) or not a YYYY-MM-DD day. |
| 400 | {"error":"endDate must be on or after startDate.","code":"invalid_input","field":"endDate"} | `endDate` is before `startDate`. |
| 400 | {"error":"The range can be at most 366 days.","code":"invalid_input","field":"endDate"} | More than 366 days. |
| 400 | {"error":"tz must be an IANA time zone, e.g. Europe/Stockholm or America/New_York.","code":"invalid_input","field":"tz"} | Unknown `tz`. |
| 400 | {"error":"type must be a comma-separated subset of drop,tip,subscription.","code":"invalid_input","field":"type"} | Unknown `type`. |
| 400 | {"error":"includeReversed must be true or false.","code":"invalid_input","field":"includeReversed"} | Anything else. |
| 400 | {"error":"Invalid cursor. Pass nextCursor from the previous page unchanged.","code":"invalid_input","field":"cursor"} | Malformed `cursor`. |
| 401 | {"error":"Unauthorized","code":"unauthorized"} | Missing or invalid API key. |
| 403 | {"error":"This endpoint needs a key bound to an approved app.","code":"app_key_required"} | A Personal key was used. |
| 403 | {"error":"Use the app owner's key for this endpoint.","code":"owner_key_required"} | The key was minted by a creator for themselves. App-level calls need a key held by your app owner: their own, or one their agency minted for its personas or managed creators. |
| 400 | {"error":"This endpoint only acts as the key’s own account — send it without X-Dropfans-Account.","code":"account_header_not_allowed"} | `X-Dropfans-Account` was sent. These endpoints always answer for the key’s app. |
Rate limiting
Every response carries the X-RateLimit-Tier header and, on limited tiers, the per-minute and per-day trios — read X-RateLimit-Remaining and X-RateLimit-Reset instead of hardcoding limits. Details in Rate limits.
Code samples
curl "https://www.dropfans.io/api/external/crm/transactions?tz=America%2FNew_York&type=drop%2Ctip" \
-H "Authorization: Bearer $DROPFANS_API_KEY"const res = await fetch(`https://www.dropfans.io/api/external/crm/transactions?tz=America%2FNew_York&type=drop%2Ctip`, {
headers: {
Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,
},
});
console.log(await res.json());import os
import requests
res = requests.get(
"https://www.dropfans.io/api/external/crm/transactions",
headers={"Authorization": f"Bearer {os.environ['DROPFANS_API_KEY']}"},
params={"tz": "America/New_York", "type": "drop,tip"},
)
print(res.json())Questions? [email protected]
