Trace

Trace API

Connect your own tools to Trace. Make a key in the app, then list fans, request one marked copy per buyer, check leaks and read your usage from a CRM, a script or an automation.

Base URL: https://api.creatorsenseai.com/api/v1/trace/v1

Machine-readable spec: trace-v1.openapi.json (OpenAPI 3.1, generated from the API itself).

A key acts as your account. Same plan, same limits, and marked copies count the same as copies made in the app.

Contents

  1. Quick start
  2. Authentication
  3. Scopes
  4. Rate limits
  5. Errors
  6. How marked copies are counted
  7. Fans
  8. Masters and copies
  9. Jobs
  10. Leaks and cases
  11. Plan and usage
  12. Questions

Quick start

  1. In the app, open Settings → API keys. Give the key a name, pick scopes, and create it.
  2. Copy the key straight away. It starts with cs_live_ and is shown once.
  3. Call the API with it:
export TRACE_KEY="cs_live_..."
curl https://api.creatorsenseai.com/api/v1/trace/v1/usage \
  -H "Authorization: Bearer $TRACE_KEY"

The usual job, one marked copy for each buyer of a drop, is four calls: get an upload URL and PUT the file, prepare the master, poll the job until it succeeds, then request copies for your fan IDs and download each one.

Authentication

Send the key on every request, in either header:

Authorization: Bearer cs_live_...
X-API-Key: cs_live_...
  • Keys belong to the account that made them. An account can have up to 10 active keys.
  • The full key is shown once, when you create it. CreatorSense stores only a hash, so a lost key can't be shown again: revoke it and make a new one.
  • You can set an expiry date. Revoked and expired keys stop working at once and get a 401 that says which.
  • The account owner gets an email whenever a key is created or revoked. Each key shows when it was last used.
  • Keys only work on the API on this page. They can't sign in to the app, and they can't create, list or revoke keys. That needs a signed-in session.
  • Keep keys on a server or in a tool's secret settings, never in a web page or an app bundle.

Scopes

ScopeAllows
trace:readList fans, masters, copies, jobs, cases, and read plan and usage. Get copy download URLs.
trace:writeAdd and import fans, upload files, prepare masters, request copies, submit leaks.

Scopes don't imply each other. Most integrations need both; a reporting tool needs only trace:read. A call without the right scope gets a 403 with INSUFFICIENT_SCOPE.

Rate limits

Each key can make 120 requests a minute. Every response, errors included, carries:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1792985100   (Unix time the minute resets)

Over the limit you get 429 with RATE_LIMITED and a Retry-After header in seconds. Leak checks also share the account's limit of 30 a minute (DETECT_RATE_LIMIT), the same as in the app.

Errors

Every error has the same JSON shape:

{
  "error": {
    "code": "INSUFFICIENT_SCOPE",
    "message": "This key does not have the trace:write scope.",
    "status": 403,
    "required_scope": "trace:write"
  }
}

Branch on code; message is for people and may change.

StatusCodesWhat to do
401API_KEY_MISSING, API_KEY_INVALID, API_KEY_REVOKED, API_KEY_EXPIRED, ACCOUNT_INACTIVECheck the header, or make a new key.
402Plan limits, for example TRACE_FREE_FAN_LIMIT, TRACE_TRIAL_COPY_LIMIT, TRACE_VAULT_OVERAGE_LIMITThe same limits as the app. Change plan or wait for the next month.
403INSUFFICIENT_SCOPE, TRACE_FORBIDDENUse a key with the scope; the resource belongs to another account.
404NOT_FOUND, FAN_NOT_FOUNDCheck the ID.
409MASTER_NOT_READYPoll the master's job, then try again.
413USE_PRESIGNThe file is too big for a direct upload. Use POST /uploads.
422VALIDATION_ERROR (with a fields list)Fix the request body or query.
429RATE_LIMITED, DETECT_RATE_LIMITWait for Retry-After.
5xxTRACE_UNAVAILABLE, INTERNAL_ERRORRetry with backoff.

How marked copies are counted

The API uses the same meter as the app. A copy made through the API and a copy made in the app land on the same monthly counter, under the same plan rules on the pricing page (and agency pricing for pooled agency plans).

  • On current plans, each copy for one fan is one marked copy. Your plan's included copies are used first, then copy-pack credits, then overage up to your monthly ceiling.
  • Preparing a master doesn't use a marked copy. Requesting copies from it does.
  • On the Free plan, each master you prepare uses one of the month's free marked drops, as it does in the app.
  • Leak checks and reading cases are never counted as copies.
  • A request that fails (for example a master that isn't ready) counts nothing.
  • Plans bought before the current pricing keep their original terms through the API too.

Use GET /usage to see where you are this month.

Fans

A fan is a buyer or subscriber you send content to. Each fan has a mark ID that their copies carry.

GET /fans trace:read

Every active fan on the account.

curl https://api.creatorsenseai.com/api/v1/trace/v1/fans \
  -H "Authorization: Bearer $TRACE_KEY"
{
  "fans": [
    {
      "id": "6f1c2a90-2b5e-4c7e-9a43-0d6f2c1e8b11",
      "label": "@bella_fan",
      "display_name": null,
      "platform": "onlyfans",
      "watermark_id": 7,
      "tier": "standard",
      "created_at": "2026-10-20T09:12:44+00:00"
    }
  ],
  "count": 1
}

POST /fans trace:write

Add one fan. A mark ID is assigned for you. Your plan's fan limit applies.

curl https://api.creatorsenseai.com/api/v1/trace/v1/fans \
  -H "Authorization: Bearer $TRACE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label": "@mike_22", "platform": "fansly"}'
{
  "id": "a3d9e0c4-7f61-4b0b-8a2e-5c9b7d3f1e20",
  "label": "@mike_22",
  "display_name": null,
  "platform": "fansly",
  "watermark_id": 8,
  "tier": "standard",
  "created_at": "2026-10-26T03:01:10+00:00"
}

POST /fans/import trace:write

Add many fans at once: up to 200 in labels, or up to 2,000 names pasted in text (one per line, or a CSV of handles). Names you already have are skipped, not duplicated. Anything not imported is listed in skipped with a reason.

curl https://api.creatorsenseai.com/api/v1/trace/v1/fans/import \
  -H "Authorization: Bearer $TRACE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"labels": ["@bella_fan", "@cara", "@dan.k"]}'
{
  "created": [
    { "id": "…", "label": "@cara", "display_name": null, "platform": null,
      "watermark_id": 9, "tier": "standard", "created_at": "2026-10-26T03:02:00+00:00" },
    { "id": "…", "label": "@dan.k", "display_name": null, "platform": null,
      "watermark_id": 10, "tier": "standard", "created_at": "2026-10-26T03:02:00+00:00" }
  ],
  "skipped": [ { "label": "@bella_fan", "reason": "already_exists" } ],
  "created_count": 2,
  "skipped_count": 1,
  "message": null
}

Masters and copies

A master is the clean file you upload once. A copy is the marked copy made from it for one fan. Copies are normal MP4 files you send the way you always do.

POST /uploads trace:write

A short-lived URL to upload a file to. purpose is master (a file to make copies from) or leak (a file to check). PUT the file body to upload_url with the headers given, then use object_key.

curl https://api.creatorsenseai.com/api/v1/trace/v1/uploads \
  -H "Authorization: Bearer $TRACE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"purpose": "master", "filename": "march-drop.mp4", "content_type": "video/mp4"}'
{
  "object_key": "trace/staging/encode/…/march-drop.mp4",
  "upload_url": "https://…r2.cloudflarestorage.com/…",
  "method": "PUT",
  "headers": { "Content-Type": "video/mp4" },
  "expires_at": "2026-10-26T04:00:00+00:00",
  "max_size": 2000000000
}
curl -X PUT "$UPLOAD_URL" -H "Content-Type: video/mp4" --data-binary @march-drop.mp4

POST /masters trace:write

Start preparing an uploaded master. Returns 202 and a job to poll.

curl https://api.creatorsenseai.com/api/v1/trace/v1/masters \
  -H "Authorization: Bearer $TRACE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"object_key": "trace/staging/encode/…/march-drop.mp4", "title": "March drop"}'
{
  "master_id": "0b7e6a52-1c3d-4f8e-9a10-2d4c6e8f0a13",
  "job_id": "c4e1b7a0-9d2f-4e6b-8c31-7a5f0e2d9b44",
  "status": "pending"
}

GET /masters/{master_id} trace:read

A master and its status (pending, then ready, or failed).

curl https://api.creatorsenseai.com/api/v1/trace/v1/masters/$MASTER_ID \
  -H "Authorization: Bearer $TRACE_KEY"
{
  "id": "0b7e6a52-1c3d-4f8e-9a10-2d4c6e8f0a13",
  "title": "March drop",
  "status": "ready",
  "media_kind": "video",
  "created_at": "2026-10-26T03:05:00+00:00"
}

POST /masters/{master_id}/copies trace:write

One marked copy per fan, from a ready master. Send up to 1,000 fan IDs; a repeated ID is made once. Each copy counts as one marked copy (see above). If the master isn't ready yet you get 409 MASTER_NOT_READY and nothing is counted. Files are built when first downloaded, so this returns quickly.

curl https://api.creatorsenseai.com/api/v1/trace/v1/masters/$MASTER_ID/copies \
  -H "Authorization: Bearer $TRACE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fan_ids": ["6f1c2a90-2b5e-4c7e-9a43-0d6f2c1e8b11", "a3d9e0c4-7f61-4b0b-8a2e-5c9b7d3f1e20"]}'
{
  "copies": [
    {
      "id": "5d2f8b1e-3a7c-4e90-b6d4-1f0a9c8e7b62",
      "master_id": "0b7e6a52-1c3d-4f8e-9a10-2d4c6e8f0a13",
      "fan_id": "6f1c2a90-2b5e-4c7e-9a43-0d6f2c1e8b11",
      "label": "@bella_fan",
      "ready": false,
      "download_url": "/api/v1/trace/v1/copies/5d2f8b1e-3a7c-4e90-b6d4-1f0a9c8e7b62/download"
    }
  ],
  "count": 1
}

GET /masters/{master_id}/copies trace:read

Every copy made from a master, in the same shape.

curl https://api.creatorsenseai.com/api/v1/trace/v1/masters/$MASTER_ID/copies \
  -H "Authorization: Bearer $TRACE_KEY"

GET /copies/{copy_id}/download trace:read

A signed URL for the copy's file, valid for expires_in seconds. The first call for a copy builds the file and can take a little longer. Downloading again costs nothing.

curl https://api.creatorsenseai.com/api/v1/trace/v1/copies/$COPY_ID/download \
  -H "Authorization: Bearer $TRACE_KEY"
{ "url": "https://…/march-drop_5d2f8b.mp4?X-Amz-Signature=…", "expires_in": 900 }

Jobs

There are no webhooks. Anything that takes time returns an ID to poll. Every few seconds is plenty.

GET /jobs/{job_id} trace:read

type is encode (a master), leak_fetch (fetching a leak URL) or leak_check. status is queued, running, succeeded or failed. A leak_fetch job gives the leak_check it started in next_job_id.

curl https://api.creatorsenseai.com/api/v1/trace/v1/jobs/$JOB_ID \
  -H "Authorization: Bearer $TRACE_KEY"
{
  "id": "c4e1b7a0-9d2f-4e6b-8c31-7a5f0e2d9b44",
  "type": "encode",
  "status": "succeeded",
  "master_id": "0b7e6a52-1c3d-4f8e-9a10-2d4c6e8f0a13",
  "case_id": null,
  "next_job_id": null,
  "error": null,
  "created_at": "2026-10-26T03:05:00+00:00",
  "finished_at": "2026-10-26T03:06:41+00:00"
}

Leaks and cases

Submitting a leak opens a case. When the check finishes, the case says what Trace found. A matched verdict means the leaked file matches the copy delivered to one account. That shows which copy leaked, not who shared it: one login can be shared. Any other verdict names nobody, and no_mark_found is inconclusive, not a clearance.

POST /leaks trace:write

Check a leak by url (a page or file link), or by object_key for a file you uploaded with POST /uploads and "purpose": "leak". Give exactly one. master_id is optional.

curl https://api.creatorsenseai.com/api/v1/trace/v1/leaks \
  -H "Authorization: Bearer $TRACE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example-leak-site.com/v/123"}'
{
  "case_id": "9e3a1f70-4c2b-4d8e-a65f-0b1c2d3e4f50",
  "job_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "status": "queued"
}

POST /leaks/upload trace:write

Upload a small leaked file directly as multipart/form-data (field file, optional master_id). Files over 8 MB get 413 USE_PRESIGN: use POST /uploads instead.

curl https://api.creatorsenseai.com/api/v1/trace/v1/leaks/upload \
  -H "Authorization: Bearer $TRACE_KEY" \
  -F "file=@leaked-clip.mp4"

GET /cases/{case_id} trace:read

The case and its result. verdict is null while the check runs. match is present only for a matched verdict. If your plan's monthly match limit holds the result back, attribution_locked is true and match is null, as in the app.

curl https://api.creatorsenseai.com/api/v1/trace/v1/cases/$CASE_ID \
  -H "Authorization: Bearer $TRACE_KEY"
{
  "id": "9e3a1f70-4c2b-4d8e-a65f-0b1c2d3e4f50",
  "status": "done",
  "verdict": "matched",
  "summary": "The leaked file matches the copy delivered to the matched account. This shows which copy leaked, not who shared it; one login can be shared.",
  "source_url": "https://example-leak-site.com/v/123",
  "master_id": "0b7e6a52-1c3d-4f8e-9a10-2d4c6e8f0a13",
  "match": {
    "watermark_id": 7,
    "fan_id": "6f1c2a90-2b5e-4c7e-9a43-0d6f2c1e8b11",
    "fan_label": "@bella_fan",
    "statement": "The leaked file matches the copy delivered to this account. One login can be shared, so this is not proof of who shared it."
  },
  "attribution_locked": false,
  "created_at": "2026-10-26T03:20:00+00:00"
}

GET /cases trace:read

Your cases, newest first. limit (1 to 200, default 50) and offset.

curl "https://api.creatorsenseai.com/api/v1/trace/v1/cases?limit=20" \
  -H "Authorization: Bearer $TRACE_KEY"
{ "cases": [ { "id": "9e3a1f70-…", "status": "done", "verdict": "matched", "…": "…" } ], "limit": 20, "offset": 0 }

Plan and usage

GET /usage trace:read

This month's marked copies (UTC month): what your plan includes, what you've used (app and API together), copy-pack credits left, and overage so far. allowance is null when copies aren't metered.

curl https://api.creatorsenseai.com/api/v1/trace/v1/usage \
  -H "Authorization: Bearer $TRACE_KEY"
{
  "plan": "pro",
  "tier": "pro",
  "pricing_version": 3,
  "band": "growth",
  "period": "2026-10",
  "copies": {
    "allowance": 1000,
    "used": 412,
    "included_left": 588,
    "pack_balance": 0,
    "pack_used_this_month": 0,
    "overage_this_month": 0,
    "overage_rate_cents": 15,
    "overage_ceiling": 2000
  },
  "leak_checks": "not metered"
}

Questions

Do I need to set anything up with you first?

No. Any account can make keys in Settings and start calling the API.

Will the API change under me?

This is version 1. Fields may be added, but existing fields keep their names and meaning. Anything that would break an integration goes in a new version with its own path.

Is there a sandbox?

Not yet. A Free account works with the API for testing, within the Free plan's limits.

Can an agency use one key for its whole roster?

A key acts as the account that made it, with that account's plan and pooled allowance. Make one key per tool, so you can revoke one without touching the others.

Something's wrong.

Email info@creatorsenseai.com with the time, the endpoint and the error code. Never send your key.