Creator API
Read your releases, supporters and payments from your own systems.
The API gives your own systems, like a members-only site, the same data you
see in your project: releases, supporters and payments. The API calls
releases creations. This page is also available as plain markdown at
https://10er.com/developers.md.
1. Overview
- One key, one project. Create API keys under your project’s Settings → API. Any team member can create and revoke them. A key belongs to that one project; nothing in the URL names the project.
- Read-only by default. Every key can read. Tick Allow creating releases
to add the
creations:writescope. - Shown once. Copy the key when you create it. We only store a hash of it.
- Revoking is immediate. A revoked key gets
401on its next request. - Bearer auth over HTTPS. Send
Authorization: Bearer YOUR_KEYon every request, and only over HTTPS. - 300 requests per minute per key, across all endpoints. Above that you
get
429; wait a minute. - Nothing is cached. Every response has
Cache-Control: no-store.
The base URL is https://10er.com/api/v1.
curl https://10er.com/api/v1/creations \
-H "Authorization: Bearer $API_KEY"
2. Conventions
- Times in responses are UTC ISO 8601:
"2026-10-01T06:00:00Z". Time filters take ISO 8601 too. Include an offset; a value without one, like2026-10-01, is read as Copenhagen time.*_afterfilters are inclusive,*_beforefilters are exclusive. - Money is a string in major units with two decimals and a dot:
"125.00"is 125 kroner. Never a float, never a comma. - Currency is an upper-case ISO 4217 code:
"DKK","EUR". - IDs are integers.
Lists and pagination
Every list returns the same envelope, ordered by id, oldest first:
{
"data": [ ... ],
"next_cursor": "4812"
}
Pass next_cursor back as after to get the next page. When next_cursor is
null, you have everything. limit defaults to 100 and is capped at 200.
New records always get higher ids, so paging is stable while records are
added.
curl "https://10er.com/api/v1/payments?after=4812&limit=200" \
-H "Authorization: Bearer $API_KEY"
Errors
Errors share one shape:
{
"error": {
"code": "not_found",
"message": "Not found"
}
}
400bad_request: a malformed parameter, like an unparseable time, a non-integer id or cursor, or an unknown status. The message says which.401unauthorized: the key is missing, wrong or revoked.403forbidden: the key lacks the scope, e.g.creations:write.404not_found: the record does not exist or belongs to another project.422invalid: the request body failed validation.errorsmaps each field to its messages.429rate_limited: more than 300 requests in a minute.
{
"error": {
"code": "invalid",
"message": "Title can't be blank and URL can't be blank",
"errors": {
"title": ["can't be blank"],
"url": ["can't be blank"]
}
}
}
3. Creations
A creation is a release: an episode, video, post or month. Releases synced from your feed and releases you add yourself are both here.
List creations
GET /api/v1/creations
published_after: only creations published at or after this time.published_before: only creations published before this time.
Skipped releases are included with "ignored": true, so matching never
silently misses one.
curl "https://10er.com/api/v1/creations?published_after=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer $API_KEY"
{
"data": [
{
"id": 3051,
"uid": "https://example.com/episodes/41",
"title": "Episode 41",
"url": "https://example.com/episodes/41",
"published_at": "2026-09-03T08:30:00Z",
"ignored": false,
"created_at": "2026-09-03T08:55:12Z",
"updated_at": "2026-09-03T08:55:12Z"
}
],
"next_cursor": null
}
uidis the release’s unique identifier within the project: the feed item’s guid for synced releases, otherwise the URL unless you chose one.urlcan benullfor releases added without one.ignoredistruewhen the release is skipped and will not be billed.
Get one creation
GET /api/v1/creations/:id returns the same fields plus a billing summary.
{
"id": 3051,
...
"payer_count": 182,
"revenue": {
"amount": "4550.00",
"application_fee": "455.00",
"credit_card_fee": "61.43",
"payout": "4033.57",
"currency": "DKK"
}
}
payer_countis the number of distinct supporters with apaidpayment covering this creation. Refunded payments do not count.revenue.amountis what remains of completed charges after refunds. A charge covering several creations is split evenly between them.revenue.payoutis the amount minus fees. It isnullwhile fees are not yet recorded for every charge.- Both stay at zero until the release is billed (section 7).
4. Supporters
GET /api/v1/supporters returns one record per subscription to your project,
including cancelled ones. Use it to grant, pause or revoke ongoing access.
{
"data": [
{
"supporter_id": 912,
"subscription_id": 20417,
"email": "ada@example.com",
"name": "Ada Lovelace",
"status": "active",
"amount": "25.00",
"currency": "DKK",
"started_at": "2025-03-14T19:02:44Z",
"cancelled_at": null,
"paused_until": null,
"updated_at": "2026-10-01T06:00:03Z"
}
],
"next_cursor": null
}
supporter_idis the person;subscription_idis this subscription. If someone cancels and signs up again, they appear twice with the samesupporter_id.nameis the name collected at signup, ornull.amountis what the supporter pays per release, or per month or week if your project charges that way.started_atis when they signed up.cancelled_atis when they cancelled. It is set only when the status iscancelled.paused_untilis when they becomeactiveagain: the end of their last pause, also when there is a gap before it. It is set only when the status ispaused.
Supporter status
pending: signed up but has not added a card yet. Shown as Incomplete in your project. Not returned unless you ask for it.prepaid: has a card and is subscribed, but has not been charged yet. Shown as Subscribed.active: has been charged at least once. Shown as Paying.paused: paused by the supporter untilpaused_until. No new payments are created until then; they becomeactiveagain automatically.suspended: their card expired or kept failing. They are not charged until they add a working card, and then becomeactiveagain.cancelled: they stopped their support.cancelled_atsays when. This is final; signing up again creates a new subscription.
Filters
status: comma-separated statuses. Defaults toactive,prepaid,paused,suspended,cancelled, so everything exceptpending. Usestatus=pending,activeto include pending ones.updated_after: only subscriptions updated at or after this time. Status changes, cancellations and pauses all updateupdated_at.
5. Payments
GET /api/v1/payments returns one record per payment. Use it to grant
access per release.
{
"data": [
{
"payment_id": 88120,
"supporter_id": 912,
"subscription_id": 20417,
"email": "ada@example.com",
"creation_ids": [3051, 3052],
"status": "paid",
"purchased_at": "2026-10-01T06:00:00Z",
"transaction_id": "ch_3Q2xYz...",
"amount": "50.00",
"currency": "DKK",
"refunded_at": null,
"refunded_amount": null,
"updated_at": "2026-10-01T06:00:03Z"
}
],
"next_cursor": null
}
- One payment can cover several creations. Everything published in the
same month is billed in one payment, so
creation_idscan hold more than one id andamountis the supporter’s amount times the number of creations. The purchase of one release is the pairpayment_idand creation id. purchased_atis when the charge completed, andtransaction_idis the Stripe charge id. Both arenulluntil the payment is charged.refunded_atandrefunded_amountare set when the charge was refunded, fully or partly. A refund covers the whole charge, so it reverses every creation in the payment at once.updated_atis the later of the payment’s and its charge’s last change.
Payment status
paid: the payment completed and its charge is not refunded.refunded: the charge was refunded, fully or partially. Comparerefunded_amountwithamountto tell which.pending: not charged yet, or retrying, or waiting for the supporter to confirm the payment.unpaid: skipped or cancelled; it will never be charged.
A refunded payment is always refunded, never paid.
Filters
creation_id: only payments covering this creation.supporter_id: only this supporter’s payments.status: comma-separated statuses. Defaults topaid,refunded. Ask forpendingorunpaidexplicitly.updated_after: only payments where the payment or its charge changed at or after this time. A refund changes the charge, so refunded payments show up here.
Who paid for creation X?
curl "https://10er.com/api/v1/payments?creation_id=3051&status=paid" \
-H "Authorization: Bearer $API_KEY"
Grant access to every email (or supporter_id) in the result. To stay in
sync, poll with updated_after set to the latest updated_at you have seen,
and status=paid,refunded. Revoke access when a payment turns refunded.
updated_after is inclusive, so expect to see the boundary record again.
6. Supporter status and payment status are independent
A supporter’s status says what happens next; a payment’s status says what
already happened. Cancelling does not revoke a paid payment: a cancelled
supporter keeps every paid payment, and the releases those payments cover
stay paid for. Their pending payments are cancelled and become unpaid.
Grant ongoing access from supporters, and per-release access from payments.
7. When a creation is billed
Nothing is charged when a creation is published or created.
- Once a month. Creations published in a month are billed together at the start of the next month, around the 1st. A release published on 14 October is billed around 1 November.
- To the supporters who were there. The run charges every subscription
that existed when the creation was published, is
prepaidoractiveat the time of the run, and has not already paid for it. Supporters who are paused, suspended or cancelled then are not charged for it. - New supporters pay from the day they join. Releases from before they joined are never charged in the monthly run. Right after signing up, a new supporter can choose to pay for a few recent releases at once. That payment is charged immediately and appears like any other payment.
- Skipped releases are never billed. Mark a release as skipped in your
project before the run and it is left out; the API shows it as
"ignored": true. This cannot be set through the API.
Until the run, a creation has no payments, payer_count is 0 and revenue
is zero.
The billing estimate returned when creating a creation (section 8) is only a guide:
- It counts the supporters who would be charged if the run happened now. It does not include supporters who join later, supporters who choose to pay at signup, or changes before the run.
- It does not check that the project is approved to collect payments.
- If this month’s run has already happened, a creation dated last month still
reports this month in
billed_in, but it is only billed if the run happens again.
8. Creating creations
POST /api/v1/creations needs a key with the creations:write scope;
other keys get 403. It creates exactly what adding a release in your
project creates. No emails are sent.
Send JSON:
title(required)url(required)uid(optional): your unique identifier for the release. Defaults tourl.published_at(optional): ISO 8601. Defaults to now.dry_run(optional):trueto validate and preview billing without saving anything.
Other fields are ignored.
curl -X POST https://10er.com/api/v1/creations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Episode 42",
"url": "https://example.com/episodes/42",
"uid": "episode-42",
"published_at": "2026-10-14T08:00:00Z"
}'
201when the creation was created.200with"existing": truewhen the project already has a creation with thatuid. It is returned unchanged; a POST never updates a creation. So retrying a POST is safe and needs no idempotency key.422when validation fails, even if theuidexists.400whenpublished_atis not ISO 8601.
Dry run
With "dry_run": true you get 200, nothing is saved, and the creation has
no id, created_at or updated_at yet:
{
"dry_run": true,
"existing": false,
"creation": {
"id": null,
"uid": "episode-42",
"title": "Episode 42",
"url": "https://example.com/episodes/42",
"published_at": "2026-10-14T08:00:00Z",
"ignored": false,
"created_at": null,
"updated_at": null
},
"billing": {
"charges_immediately": false,
"billed_in": "2026-11",
"already_billed_period": false,
"active_subscriptions": 182,
"estimated_amount": "4550.00",
"currency": "DKK"
}
}
A real create returns the same shape with "dry_run": false and the saved
creation.
charges_immediatelyis alwaysfalse.billed_inis the month of the run that bills the creation, ornullwhen no monthly run will: it is skipped, or its month has already been billed.already_billed_periodistruewhen the creation is dated before last month, so the monthly run will never pick it up.active_subscriptionsandestimated_amountestimate who that run would charge and how much. See the caveats in section 7.
If your project syncs from a feed
If releases already come in automatically from a podcast or RSS feed, a blog
or a YouTube channel, the sync creates them itself. Don’t POST those too, or
POST with the same uid the sync uses: the feed item’s guid, or the video id
for YouTube. Otherwise the sync creates a duplicate and supporters are billed
twice for the same release. GET /api/v1/creations shows the uid of every
synced release.