GoodCodes API
GoodCodes validates e-commerce discount codes by driving a real browser: it adds real products to the cart, applies each code at the discount field, and reads the site's own response. You get a definitive verdict per code, the discount it produced, and a screenshot as proof.
Interactive OpenAPI reference: /v1/docs · Manage keys: Account → API keys
Authentication
Every request carries your API key in the X-API-Key header.
Keys are minted on the account page — the secret is
shown once at creation and can be revoked at any time.
X-API-Key: sk_live_…
Usual flow
- Submit a batch — sites + codes (product URLs optional). You get a
batch_idback immediately. - Poll
GET /v1/batches/{batch_id}every few seconds (or give awebhook_urland get pinged when the batch finishes). - Read results — one entry per code with its classification, discount amount, and screenshot link.
Validation is real browser work: expect roughly 30 seconds to a few minutes per site depending on the store. The first visit to a brand-new site takes longest; repeat visits replay a learned profile and are much faster.
1 · Submit a batch
POST /v1/batches
curl -X POST https://goodcodes.io/v1/batches \
-H "X-API-Key: sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"sites": [
{
"site_url": "snowehome.com",
"codes": ["WELCOME10", "SAVE20"],
"product_urls": ["https://snowehome.com/products/sheet-set"]
},
{"site_url": "sakara.com", "codes": ["ATOMICBLONDESAKARA"]}
],
"webhook_url": "https://your-app.example/hooks/goodcodes"
}'
site_url— bare domain or full URL.codes— one or more per site; duplicates are removed case-insensitively.product_urls— optional. When given, validation starts from these product pages; when omitted, GoodCodes discovers products on the site itself.webhook_url— optional; receivesPOST {"batch_id": …, "status": "done"}on completion. Polling is the source of truth.
Response — 202 Accepted:
{"batch_id": "ed5d3a1a26c146b2…", "status": "queued", "sites": 2, "codes": 3}
2 · Poll the batch
GET /v1/batches/{batch_id}
{
"batch_id": "ed5d3a1a26c146b2…",
"status": "running", // queued | running | done
"created_at": "2026-08-02T17:45:06",
"finished_at": null,
"sites": [
{
"site_url": "https://snowehome.com",
"status": "done", // queued | running | done | failed
"error": null,
"results": [
{
"id": "6f906604fa8048ce…",
"code": "WELCOME10",
"status": "done", // pending | done
"classification": "valid",
"valid": true,
"discount_amount": 30.0,
"subtotal_before": 300.0,
"total_after": 270.0,
"currency": "USD",
"notes": null,
"charged": true,
"screenshot_url": "/v1/results/6f906604fa8048ce…/screenshot"
}
]
}
]
}
Classifications
| Classification | Meaning | Billed |
|---|---|---|
| valid | The code applied and reduced the total. discount_amount, subtotal_before, total_after are filled in. | yes |
| invalid | The site rejected the code as unknown/dead. | yes |
| expired | The site said the code has expired. | yes |
| min_spend | The code is recognized but the cart is below its spend threshold. | yes |
| conditions_not_met | The code is recognized but this cart doesn't qualify (product/category exclusions, membership, …). It may work on a different cart. | yes |
| ambiguous | The site responded but the outcome couldn't be read with confidence. | no |
| no_promo_field | The checkout was walked to its payment wall and provably has no promo/coupon entry point. | no |
| requires_signin | Every checkout route requires an account — codes can't be validated anonymously. | no |
| unsupported_flow | The store has no standard add-to-cart path (configurators, quiz funnels). | no |
| error | The run failed (site down, blocked, timeout). Not a verdict on the code. | no |
Billing
You're charged $0.10 per code
whose validation went through — products were added, the code was submitted,
and the site gave a definitive answer — regardless of the verdict. The five
billed classifications above are exactly that set. Errors, blocks, and
sites where a code can't be submitted at all cost nothing. See your ledger at
Usage or GET /v1/usage:
{"charged_codes": 12, "total": 1.20, "currency": "USD", "price_per_code": 0.10}
Screenshots
GET /v1/results/{result_id}/screenshot returns a PNG of the
cart at the moment the code was applied — the proof behind every verdict.
Add ?which=before for the pre-application state.
curl -H "X-API-Key: sk_live_…" \ https://goodcodes.io/v1/results/RESULT_ID/screenshot -o proof.png
Complete example (Python)
import time, httpx
API = "https://goodcodes.io/v1"
H = {"X-API-Key": "sk_live_…"}
batch = httpx.post(f"{API}/batches", headers=H, json={
"sites": [{"site_url": "snowehome.com", "codes": ["WELCOME10"]}],
}).json()
while True:
b = httpx.get(f"{API}/batches/{batch['batch_id']}", headers=H).json()
if b["status"] == "done":
break
time.sleep(5)
for site in b["sites"]:
for r in site["results"]:
print(site["site_url"], r["code"], r["classification"],
r["discount_amount"], r["screenshot_url"])
Endpoint summary
| Endpoint | Purpose |
|---|---|
POST /v1/batches | Submit sites + codes → batch_id |
GET /v1/batches/{id} | Batch status + per-code results |
GET /v1/batches | List your recent batches |
GET /v1/results/{id}/screenshot | Proof screenshot (PNG) |
GET /v1/usage | Charge ledger summary |
Full request/response schemas: interactive OpenAPI reference.