Skip to content
EarlyGrooves

API

A read-only HTTP API over your own releases and their figures. It is on every plan, including Free, and it is versioned — anything that would break a working client goes in a new version rather than into this one.

Last updated

Getting started

Everything lives under https://earlygrooves.com/api/v1. Create a token in Settings → API, then send it as a bearer token:

Authorization: Bearer eg_live_…

  • A token belongs to exactly one account and grants read only. There is no write scope.
  • It is shown once, when you create it, and stored hashed. If you lose it, revoke it and make another.
  • Revoking takes effect immediately.

Responses are JSON. A successful one wraps its payload in data.

Endpoints

EndpointWhat it answers
GET /releasesYour releases, newest first, with their streaming links and public URL. Takes status (published, draft or all), limit (1–100, default 25) and cursor.
GET /releases/{id}One release, in the same shape as a row of the list.
GET /releases/{id}/statsViews, unique visitors, clicks, click-through rate, clicks by platform and a daily series. Takes from and to as ISO dates — the last 30 days by default, up to 365 days apart.
GET /meYour handle, display name, plan and portfolio URL.

Pagination is by cursor, not by page number

A list response carries nextCursor when there is more. Pass it back as cursor. Offsets are not offered on purpose: adding a release half-way through paging would shift every later page, and you would silently skip a row or read one twice.

Statistics are aggregates, and only aggregates

There is no per-visit row, no visitor identifier and no IP address anywhere in this API, because none of them exists to return — see our privacy notice. Figures exclude your own visits to your own pages, so they match your dashboard exactly.

Rate limits

PlanLimit
Free60 requests a minute, 5,000 a day
Premium300 requests a minute, 50,000 a day

Both windows apply, and limits are counted per account rather than per address — three servers behind one connection share one budget, which is what the number on the plan comparison means.

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, all describing whichever window is the tighter one at that moment. Going over gets a 429 with Retry-After.

Errors

Failures come back in one shape:

{ "error": { "code": "not_found", "message": "…", "details": { } } }

StatusCodeMeaning
400bad_requestA parameter is malformed.
401unauthorizedNo token, or one we do not recognise.
403forbiddenA valid token, asking for something that is not yours.
404not_foundNo such resource.
422unprocessableWell-formed, but we cannot act on it.
429rate_limitedOver a limit. Wait for the seconds in Retry-After.
500internalOurs. Please tell us.

A release belonging to somebody else and a release that does not exist both answer the same way. That is deliberate: a distinguishable answer would let anybody with a token confirm which identifiers are real.

What v1 deliberately does not have

  • Subscribers. Fan email addresses are not reachable with a bearer token, at all. A token that leaks must not also leak a mailing list. You can export your list from the subscribers page, which is behind a real sign-in.
  • Anything that writes. Read-only is what every plan promises, and it is the half that cannot go wrong. Adding writes later would be a new scope rather than a change to this one.
  • Newsletters, campaigns, press releases and social posts. Not part of the free API promise. Adding them later is additive and breaks nothing.

Stability

/api/v1 is stable. Adding a field to a response, adding an endpoint or adding an optional parameter can happen at any time, so parse leniently and ignore what you do not recognise. Anything that would break a working client — removing a field, changing a type, changing what a parameter means — goes into /api/v2, and we will tell account holders before it does.

Using it is subject to our terms.