Cursor-paginated REST over campaigns, ad groups, ads, keywords, assets, reports and sync — across Google Ads, Microsoft Advertising, Reddit Ads and Meta Ads. One envelope, typed errors, and a written versioning policy.
Base URL
https://api.growomat.com/api/v1
Spec
GET /api/v1/openapi.json · OpenAPI 3.1
Auth
Authorization: Bearer grow_… · 13 scopes
Limits
60 req/min per token · Retry-After on 429
v1 · additive changes are never breaking · old versions run for at least 12 months
first request — GET /me
$ curl -s https://api.growomat.com/api/v1/me \ -H "Authorization: Bearer $GROWOMAT_TOKEN" { "data": { "uid": "user_abc", "email": "[email protected]", "plan": { "id": "pro" }, "scopes": ["campaigns:read", "campaigns:write"] } }
Campaigns are created locally and staged — nothing touches a live ad platform until a token holding the sync scope triggers a sync.
Settings → Developer
Generate a token with a name, an expiry of up to one year, and the minimum scopes it needs. The plaintext shows once — only its SHA-256 is stored.
# shown once — store it in a secret manager grow_live_A1B2C3D4E5F6G7H8_veryLongSecret… export GROWOMAT_TOKEN=grow_live_…
Confirm auth with /me
GET /me echoes the token’s identity, plan, and granted scopes — the fastest way to confirm your wiring.
curl -s -H "Authorization: Bearer $GROWOMAT_TOKEN" \
https://api.growomat.com/api/v1/me
# → { "data": { "uid": "user_abc", "email": "[email protected]",
# "plan": { "id": "pro", "name": "Pro" },
# "scopes": ["campaigns:read","campaigns:write"] } }POST your first campaign
Writes need their resource’s write scope. Every response uses the same envelope: { data, meta }
curl -s -X POST https://api.growomat.com/api/v1/campaigns \
-H "Authorization: Bearer $GROWOMAT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Black Friday 2026","type":"SEARCH","budget":50}'Every resource speaks the same envelope and the same cursor pagination. What the app can create, list, edit and delete — so can you.
| Resource | Routes | Operations |
|---|---|---|
| Token | /me | GET |
| Campaigns | /campaigns · /campaigns/:id | GETPOSTPATCHDELETE |
| Ad groups | /ad-groups · /ad-groups/:id | GETPOSTPATCHDELETE |
| Ads | /ads · /ads/:id | GETPOSTPATCHDELETE |
| Keywords | /keywords · /keywords/:id | GETPOSTPATCHDELETE |
| Assets | /assets · /assets/:id/attach | GETPOSTPATCHDELETE |
| Businesses | /businesses · /businesses/:id | GETPOSTPATCHDELETE |
| Reports | /reports/performance · /reports/disapprovals | GET |
| Sync | /sync · /sync/preview · /sync/:id | GETPOST |
| Conversions | /conversions/offline | GETPOST |
Writes need their resource’s :write scope. Triggering a sync additionally needs sync:write — it is the only call that touches live ad platforms. The exhaustive route list lives in /api/v1/openapi.json.
One envelope, typed errors, honest rate-limit headers, boring pagination, and a written versioning policy.
Response envelope— every endpoint
// success
{ "data": …, "meta": { "nextCursor": "1700000000", "total": 25 } }
// error — always this shape
{ "error": { "code": "INSUFFICIENT_SCOPE",
"message": "Missing required scope: campaigns:write",
"details": { "required": ["campaigns:write"] } } }Error codes— all of them
| HTTP | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request body |
| 401 | INVALID_CREDENTIALS | Bad, revoked, or expired token |
| 402 | SUBSCRIPTION_REQUIRED · SUBSCRIPTION_EXPIRED · API_ACCESS_NOT_IN_PLAN | Billing — resolve, then retry |
| 403 | INSUFFICIENT_SCOPE | Token lacks the required scope |
| 404 | NOT_FOUND | Missing, or outside the resolved Space |
| 429 | RATE_LIMITED | Minute or daily limit exceeded |
| 500 | INTERNAL_ERROR | Our fault — safe to retry |
Rate limits— on every response
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | Minute bucket for this token (default 60) |
| X-RateLimit-Remaining | Requests left in the current window |
| X-RateLimit-Reset | Unix time the window resets |
| Retry-After | Seconds to wait, sent with 429 |
Minute buckets are per-token, so one noisy script can’t starve another. The daily bucket comes from your plan and is shared across your tokens.
Versioning & Spaces— written down
Everything lives under /api/v1. Additions — new fields, endpoints, enum values — ship without notice and are never breaking. Removals and renames go to /api/v2, and old versions run for at least 12 months after a successor is announced.
Send the X-Space-Id header to address one workspace; omit it for your Personal Space. Ids outside the resolved Space return 404 — data never leaks across Spaces.
grow_live_A1B2C3D4E5F6G7H8_veryLongUrlSafeBase64SecretScoped access — 13 grants
sync:write is deliberately separate from the resource scopes — triggering a sync mutates external ad platforms and can spend real budget. A reporting token never needs it.
Token minting lives in Settings → Developer. Prefer a typed client? The SDKs are generated from this API’s spec.