Using the public API
Automate MartechFlow with the versioned public API: authenticate with a bearer key, list feeds, read runs and exports, trigger refreshes, and handle pagination and rate limits.

Basics: base URL and authentication
The public API is versioned and lives under /api/v1. Authenticate every request with your API key as a bearer token in the Authorization header. There is no separate login step for the API.
A key authenticates as your whole organization, so every endpoint operates on your org's feeds. Read endpoints need a read (or read-and-write) key; the refresh endpoint needs a read-and-write key.
- 1Create an API key in Settings (see Create an API key).
- 2Send it on every request as: Authorization: Bearer mtf_live_...
- 3Call endpoints under https://your-base-url/api/v1/.
Endpoints
The surface is intentionally small and stable, decoupled from internal changes so your integration does not break when we evolve the database.
- GET /api/v1/feeds: list your org's feeds (paginated). Each feed includes id, name, status, schedule (cron, or null for manual), project_id, and timestamps.
- GET /api/v1/feeds/:id: one feed's details.
- GET /api/v1/feeds/:id/runs: the feed's run history with status, trigger, product stats, error, and timing.
- GET /api/v1/feeds/:id/exports: the feed's output feeds, each with its channel, format, status, item count, last-built time, and the stable public pull URL channels fetch.
- POST /api/v1/feeds/:id/refresh: trigger a refresh (re-ingest). Requires a write key. Returns 202 Accepted with the queued feed id.
Example requests
List feeds:
- curl -H "Authorization: Bearer mtf_live_xxx" https://your-base-url/api/v1/feeds
- Read a feed's exports (to get pull URLs): curl -H "Authorization: Bearer mtf_live_xxx" https://your-base-url/api/v1/feeds/FEED_ID/exports
- Trigger a refresh (needs a write key): curl -X POST -H "Authorization: Bearer mtf_live_xxx" https://your-base-url/api/v1/feeds/FEED_ID/refresh
Pagination
The feeds list is cursor-paginated. Pass an optional limit (default 50, maximum 200) and an opaque cursor. The response includes a nextCursor; pass it back as the cursor to get the next page, and stop when nextCursor is null.
Cursors are opaque keyset tokens, not page numbers. Do not construct or parse them yourself; just echo back the nextCursor value you received.
- First page: GET /api/v1/feeds?limit=100
- Next page: GET /api/v1/feeds?limit=100&cursor=THE_NEXT_CURSOR
- A tampered or invalid cursor returns an empty page rather than an error.
Rate limits and scopes
Each key is rate-limited per fixed one-minute window (600 requests per minute by default). Exceeding it returns 429 Too Many Requests with a Retry-After header telling you how many seconds until the window resets. Back off and retry after that.
Operation access is enforced by scope: a read-only key calling a write endpoint gets 403 Forbidden. Hitting a write endpoint that your key supports but with a missing or invalid key returns 401 Unauthorized.
- 401 Unauthorized: missing, invalid, expired, or revoked key.
- 403 Forbidden: the key lacks the scope for that operation (for example a read key calling refresh).
- 404 Not Found: the feed id is not in your organization or does not exist.
- 429 Too Many Requests: rate limit hit; honor Retry-After.
Behavior to expect
A refresh you trigger via the API is a manual run; it appears in run history with a manual trigger and does not change the feed's schedule. It is queued and processed asynchronously, which is why it returns 202 rather than waiting for the refresh to finish.
Triggering a refresh updates your output files at their stable pull URLs, but the ad channels still fetch on their own schedule. The API refreshes your data; the channels decide when to pull it. Refreshing the same feed repeatedly does not stack up duplicate work at the queue level.