Overview

Every route of the v1 API on one screen, and the rules all of them share.

The base URL is https://api.submitator.com/v1. Every route below takes your key in Authorization: Bearer $SUBMITATOR_API_KEY, except GET /openapi.json.

Routes

Each path links to its full reference: parameters, the request and its responses, and the errors it returns.

Basics
GET/pingCheck your key
GET/accountRead your account
GET/openapi.jsonDownload this reference
Projects
GET/projectsList projects
POST/projectsCreate a project
GET/projects/{project_id}Read a project
PATCH/projects/{project_id}Update a project
DELETE/projects/{project_id}Delete a draft
PUT/projects/{project_id}/logoSet the logo
DELETE/projects/{project_id}/logoRemove the logo
POST/projects/{project_id}/screenshotsAdd a screenshot
DELETE/projects/{project_id}/screenshots/{image_id}Remove a screenshot
Launch
GET/projects/{project_id}/readinessPreview a launch
POST/projects/{project_id}/launchLaunch a project
GET/projects/{project_id}/listingsList a project's listings
POST/projects/{project_id}/listingsAdd directories
GET/listings/{listing_id}Read a listing
Badge
GET/projects/{project_id}/badgeGet the badge install kit
POST/projects/{project_id}/badge/verifyVerify the badge
Reports
POST/projects/{project_id}/reportsBuild a report
GET/reports/{report_id}Read a report
GET/reports/{report_id}/fileDownload a report file
Events
GET/eventsList events
GET/events/{event_id}Read an event
Webhooks
GET/webhook_endpointsList webhook endpoints
POST/webhook_endpointsAdd a webhook endpoint
GET/webhook_endpoints/{webhook_endpoint_id}Read a webhook endpoint
PATCH/webhook_endpoints/{webhook_endpoint_id}Update a webhook endpoint
DELETE/webhook_endpoints/{webhook_endpoint_id}Delete a webhook endpoint
POST/webhook_endpoints/{webhook_endpoint_id}/rotate_secretRoll the signing secret
POST/webhook_endpoints/{webhook_endpoint_id}/testSend a test event
GET/webhook_endpoints/{webhook_endpoint_id}/deliveriesList deliveries
POST/webhook_deliveries/{webhook_delivery_id}/retryRetry a delivery
Directories
GET/directoriesList directories
GET/directories/{directory_id}Read a directory
Test mode
POST/test_helpers/listings/{listing_id}/advanceMove a test listing forward
POST/test_helpers/account/launchesSet your test launches
Share links
GET/projects/{project_id}/share_linkRead the share link
POST/projects/{project_id}/share_linkCreate a share link
DELETE/projects/{project_id}/share_linkTurn the share link off
Logins
GET/projects/{project_id}/loginsList a project's logins
GET/listings/{listing_id}/emailsList the emails a login received

Requests

Send JSON with Content-Type: application/json. Image uploads also take multipart/form-data with a file field; any other body type returns 415 unsupported_media_type.

A parameter the operation does not list returns 400 unknown_parameter and changes nothing. An empty string counts as null.

Objects and lists

Every object names its type in object and its mode in livemode:

{ "object": "project", "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP", "livemode": true }

A list wraps its items in the same envelope every time:

{
  "object": "list",
  "data": [{ "object": "listing", "id": "lst_7Hk2Qn4Rt9Vx1Bm6Cz3LdK" }],
  "has_more": true,
  "next_cursor": "cur_8Tq2Xn5Wb1Lz7Pd3"
}

Ids

An id is a type prefix and 22 letters and digits, such as prj_4QzX1m9Lr2Vb7Nc8Tk3HwP. Treat ids as opaque strings: compare them whole and store them as text.

PrefixObject
prj_Project
lst_Listing
dir_Directory
img_Image: a logo or a screenshot
rep_Report
evt_Event
we_Webhook endpoint
wd_Webhook delivery
key_API key, as GET /ping shows it

Keep your own reference in external_id: up to 100 characters, unique in your account, and searchable with GET /projects?external_id=crm_8812. metadata holds up to 20 more keys of your own.

Pagination

Lists return up to limit items: 1 to 100, and 20 when you leave it out. When has_more is true, send next_cursor back as cursor to read the next page:

curl "https://api.submitator.com/v1/projects?limit=50&cursor=cur_8Tq2Xn5Wb1Lz7Pd3" \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY"

Cursors are opaque and belong to the list that issued them: a cursor from another list returns 400 invalid_cursor. The events feed always returns a next_cursor, so you can poll it for what is new.

Retry a POST safely

Send an Idempotency-Key header, such as a UUID, with a POST you may send twice: creating a project, launching, adding directories, building a report or adding a webhook endpoint. A repeat with the same key and the same body within 24 hours returns the first response again, with Idempotent-Replayed: true, and changes nothing twice.

curl https://api.submitator.com/v1/projects \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY" \
  -H "Idempotency-Key: 4f9d2c1e-ledgerly-create" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://ledgerly.test", "contact_email": "founder@ledgerly.test"}'

The same key with another body returns 422 idempotency_key_reused. A repeat while the first request still runs returns 409 idempotency_key_in_use: wait 1 second and send it again.

Test mode

A sbm_test_ key runs every route below on test data, where listings move on their own and nothing is spent. The routes under /test_helpers work only there and return 403 test_mode_only with a live key. Test mode says what is simulated.

Rate limits

Each account and mode can make 3,600 requests per UTC hour, or 600 before your first purchase. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, and Rate limits lists the operations with limits of their own.

Request ids

Every response carries an X-Request-Id header, and error bodies repeat it as error.request_id. Log it with each call and quote it when you write to support.

Time

Times are ISO 8601 in UTC with a Z, such as 2026-10-08T14:00:00Z. Listing times are rounded down to the hour, and an event's created_at is exact to the second.

Dates without a time, such as launch_date, are YYYY-MM-DD.

Errors

Every 4xx and 5xx response has the same body, with a stable code to branch on:

{
  "error": {
    "code": "not_found",
    "message": "No project prj_4QzX1m9Lr2Vb7Nc8Tk3HwQ in live mode. Check the id and the key's mode.",
    "param": "project_id",
    "doc_url": "https://submitator.com/docs/errors#not_found",
    "request_id": "0133b7c4-6a56-4793-8ada-5f7c570eefa3"
  }
}

Error format explains each field, and Error codes lists every code.

The OpenAPI document

GET /openapi.json returns this reference as an OpenAPI 3.1 document with the released operations. It needs no key and can be cached for 1 hour: generate a client from it, or load it into your API tool.

curl https://api.submitator.com/v1/openapi.json -o submitator-v1.json