Errors, limits & paging
Conventions every endpoint follows: JSON shapes, error types, rate limits, pagination, dates and versioning.
Requests and responses
- Base URL
https://api.aceversity.com/v1. JSON in, JSON out (Content-Type: application/json). GETandDELETEtake query parameters;POST,PUTandPATCHtake a JSON body. Path parameters are always ids.PATCHchanges only the fields you send. Sendnullto clear an optional field (e.g."due_on": null).PUT /v1/courses/{id}/grade-categoriesreplaces the whole list.- Successful creates return
201with the new object; deletes return200 {"id": "…", "deleted": true}. - Unknown fields are ignored. Ids are UUIDs.
Dates and time zones
- Calendar dates are
YYYY-MM-DD(due_on). Timestamps are ISO 8601 in UTC (created_at). - "Today" is the member's today in their profile time zone —
GET /v1/mereturns it astoday.days_untiland/v1/agendause it, so a student in Los Angeles at 7 pm on the 12th still sees the 12th.
Errors
Every error has the same shape:
{
"error": {
"type": "validation_failed",
"message": "`due_on`: Invalid ISO date",
"request_id": "req_8f2a1c9b0d3e4f5a6b7c",
"details": [{ "field": "due_on", "message": "Invalid ISO date" }]
}
}
| Status | type |
Meaning / what to do |
|---|---|---|
| 400 | invalid_request |
Malformed JSON, wrong content type, bad cursor. Fix the request. |
| 401 | unauthorized |
Missing, invalid, expired or revoked credential. Re-authenticate (OAuth: refresh, then re-authorize). |
| 403 | insufficient_scope |
The credential lacks write. Ask the member to reconnect with edit access. |
| 403 | membership_required |
Subscription not active. Tell the member; don't retry. |
| 404 | not_found |
No such route, or the object doesn't exist or isn't this member's (we don't distinguish). |
| 405 | method_not_allowed |
See the Allow header. |
| 409 | conflict |
A limit was reached (30 courses, 20 milestones per item). |
| 422 | validation_failed |
Input failed validation; details lists each field. |
| 429 | rate_limited |
Wait Retry-After seconds. |
| 500 | internal_error |
Our fault. Retry with backoff; include request_id if you contact support. |
Every response has an X-Request-Id header. Quote it to support@aceversity.com.
Rate limits
120 requests per minute per credential (each API key or OAuth token), fixed one-minute windows. Every response tells you where you are:
RateLimit-Limit: 120
RateLimit-Remaining: 117
RateLimit-Reset: 42
Over the limit you get 429 with Retry-After. The OAuth endpoints have their own per-IP limits (token: 60/min, registration: 20/hour). Sign-in pages: 15 attempts per 10 minutes per IP.
Recommended client behaviour: on 429 sleep Retry-After; on 5xx or network errors retry up to 3 times with exponential backoff (1 s, 2 s, 4 s) — only for GET, PUT, PATCH and DELETE, which are idempotent. Don't blindly retry POST (you could create a duplicate).
Pagination
List endpoints return:
{ "data": [ … ], "has_more": true, "next_cursor": "MjU" }
Pass limit (1–100, default 25) and cursor=<next_cursor> for the next page. Stop when has_more is false. Small collections (courses, decks, milestones) return everything in one page.
Polling for changes
For automations ("when a new deadline is added, do X"), call GET /v1/items?created_after=<ISO timestamp of your last run> on a schedule. Store the newest created_at you saw and pass it next time. (Webhooks are on the roadmap.)
Versioning
- The version is in the path:
/v1. Within v1 we only make additive changes — new endpoints, new optional parameters, new response fields, new enum values (handle unknownkindvalues gracefully). - Breaking changes would ship as
/v2with at least 6 months of overlap, announced in the changelog and by email to members with active keys. info.versioninopenapi.jsonfollows semver.