List Subscription Charges
GET/merchants/:merchantId/chargesDescription
List all subscription charges for the merchant — both succeeded and failed. Results are returned in descending order by a monotonic internal sequence, so late-arriving rows always land at the head and never fall into already-paged regions.
Use this endpoint for:
- Per-subscription health dashboards (filter by
subscription_id) - Dunning workflows (filter by
status=failed) - CSV exports for reconciliation (
from/todate range) - Per-customer activity views (filter by
subscriberorexternal_customer_id)
Headers
| Header | Description | Required |
|---|---|---|
| Authorization | Bearer token with your API key | yes |
Path Parameters
| Name | Type | Description | Required |
|---|---|---|---|
| merchantId | string | Your merchant ID (visible in the dashboard). | yes |
Query Parameters
| Name | Type | Description | Required |
|---|---|---|---|
| limit | number | Maximum number of charges to return (default: 50, max: 100). | no |
| starting_after | string | Cursor for pagination. Returns charges after this charge ID (`subc_...`). | no |
| ending_before | string | Cursor for pagination. Returns charges before this charge ID. | no |
| status | string | Filter by status: `succeeded` or `failed`. | no |
| kind | string | Filter by kind: `cycle` or `adhoc`. | no |
| subscription_id | string | Filter by parent subscription. | no |
| subscriber | string | Filter by on-chain subscriber address. | no |
| external_customer_id | string | Filter by the merchant-supplied customer reference (matched on the parent subscription). | no |
| chain | string | CAIP-2 chain identifier (e.g. `eip155:1`). | no |
| from | string | ISO 8601 timestamp. Inclusive lower bound on `charged_at` (block timestamp on success, API submission time on failure). | no |
| to | string | ISO 8601 timestamp. Inclusive upper bound on `charged_at`. | no |
Example Request
const response = await fetch(
'https://checkout.exodus.com/merchants/mer_42/charges?limit=20&status=failed',
{
headers: {
Authorization: 'Bearer sk_live_xxxxxxxxxxxxxxxx',
},
},
);Response
SUCCESSFUL RESPONSE
{
"object": "list",
"data": [
{
"object": "subscription_charge",
"id": "subc_7890abcdef123456",
"subscription_id": "sub_abc123def456",
"subscriber": "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE21",
"amount": "9990000",
"fee": null,
"tx_hash": "0xdef...",
"chain": "eip155:1",
"charge_nonce": 4,
"charged_at": "2026-06-19T12:00:00Z",
"status": "failed",
"kind": "cycle",
"failure_reason": "InsufficientBalance"
},
{
"object": "subscription_charge",
"id": "subc_6789abcdef012345",
"subscription_id": "sub_abc123def456",
"subscriber": "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE21",
"amount": "9990000",
"fee": "0",
"tx_hash": "0xabc...",
"chain": "eip155:1",
"charge_nonce": 3,
"charged_at": "2026-05-19T12:02:18Z",
"status": "succeeded",
"kind": "cycle",
"failure_reason": null
}
],
"has_more": true
}Pagination Guarantees
The cursor is strictly monotonic in ingestion order, not on-chain time. This means:
- A row written by the indexer’s late-backfill path (a merchant who submits charges directly from their own RPC, captured by Alchemy webhook minutes later) always lands at the head of the list.
- Resuming pagination days later never loses rows from already-paged regions.
- The
from/torange filter targetscharged_at(block timestamp), not ingestion time, so date-range exports remain stable.
Merchants should still deduplicate by id when reconciling — Alchemy webhook redeliveries are
swallowed by tx_hash uniqueness, but late-arriving rows whose charged_at falls inside an
already-queried date range will re-appear at the head on subsequent queries.
next page
const nextPage = await fetch(
'https://checkout.exodus.com/merchants/mer_42/charges?limit=20&starting_after=subc_6789abcdef012345',
{
headers: {
Authorization: 'Bearer sk_live_xxxxxxxxxxxxxxxx',
},
},
);