The MythicDeals API lets your own code do what you do in the Seller hub: keep stock and prices in sync with other marketplaces, publish and edit listings in bulk, load instant-delivery accounts and gift card codes, deliver and correct orders, answer buyers and disputes, follow your payouts and close your store for a break. Webhooks tell your server the moment an order is paid, a buyer writes or a dispute opens.
openapi.json into Postman, Insomnia or a code generator.
What you can automate
- OffersCreate, duplicate, edit, pause and delete listings, or import hundreds from a CSV file. The catalog tells you which attributes each category needs, so every listing is valid the first time.
POST /listingsPOST /listings/{id}/duplicatePOST /listings/import/preview - Stock & pricesUpdate up to 100 listings per call, or every listing that matches a filter (e.g. −5 % on one game) after a dry run. See how your price ranks against the other offers for the same item.
PATCH /listings/bulkPOST /listings/bulk-updateGET /listings/{id}/competitors - DeliveriesLoad accounts and gift card codes for instant delivery, start and deliver orders, correct a delivery, cancel with a full refund, and download invoices.
POST /listings/{id}/unitsPOST /orders/{order}/deliverPATCH /orders/{order}/delivery - ChatRead and answer the order chat, with files, and get a webhook the moment a buyer writes.
GET /orders/{order}/messagesPOST /orders/{order}/messagesmessage.created - DisputesRead the case, its evidence and timeline; accept, make a counter-offer (refund or redelivery), reply with evidence or escalate to MythicDeals.
GET /disputesPOST /disputes/{id}/counterdispute.created - PayoutsBalances, every ledger entry and the status of each withdrawal, from request to transfer.
GET /walletGET /wallet/ledgerGET /payouts - AvailabilityAppear offline, close your store for a break with a reopening date, and check what currently stops you from selling.
GET /accountPUT /account/statusPOST /account/breaks
Quick start
- Create a token. Go to Settings → API access, name the token after the tool that will use it, tick only the permissions (scopes) it needs and pick an expiry (30 days, 90 days, 365 days or never). The token starts with
go_and is shown once — copy it into your tool’s secret store. - Call the API. Check that the token works with
GET /me:export MYTHICDEALS_TOKEN=go_your_token_here curl 'https://mythicdeals.com/api/v1/me' \ -H "Authorization: Bearer $MYTHICDEALS_TOKEN" \ -H "Accept: application/json"const res = await fetch('https://mythicdeals.com/api/v1/me', { headers: { Authorization: `Bearer ${process.env.MYTHICDEALS_TOKEN}`, Accept: 'application/json', }, }); const json = await res.json();import os import requests res = requests.get( "https://mythicdeals.com/api/v1/me", headers={ "Authorization": f"Bearer {os.environ['MYTHICDEALS_TOKEN']}", "Accept": "application/json", }, ) print(res.status_code, res.json())use Illuminate\Support\Facades\Http; $res = Http::withToken(env('MYTHICDEALS_TOKEN')) ->acceptJson() ->get('https://mythicdeals.com/api/v1/me'); $res->throw(); $data = $res->json('data'); - Build. Read the catalog (
GET /games, then the category attributes) to create valid listings, sync your stock withPATCH /listings/bulk, and add a webhook fororder.paidanddispute.createdso you can deliver and answer without polling.
Base URL & format
All endpoints live under one base URL, over HTTPS only:
https://mythicdeals.com/api/v1
- Requests and responses are JSON. Send
Accept: application/jsonand, with a body,Content-Type: application/json. Uploads (listing photos, delivery proofs, chat files) usemultipart/form-data. - A single object comes back as
{"data": {…}}, a list as{"data": […]}pluslinksandmetawhen it is paginated. - Unknown fields in your request are ignored. Empty values in responses are
nullrather than missing; a few fields only appear in some cases (for example a webhooksecretright after it is created).
Authentication
Send your token in the Authorization header of every request:
GET /api/v1/me HTTP/1.1
Host: mythicdeals.com
Authorization: Bearer go_your_token_here
Accept: application/json
- A missing, wrong, expired or revoked token gets
401 unauthenticated. A suspended account gets403 account_suspended. - You can have up to 10 active tokens. We store only a fingerprint (hash) of each token, email you when a new one is created, and show when and from which IP it was last used.
- Give each tool its own token and revoke it on the API access page as soon as you stop using that tool. A revoked token stops working immediately.
- Never put a token in a browser, mobile app or public repository — anyone holding it can act on your account within its scopes.
Scopes
A token can only call endpoints whose scope it has; otherwise the answer is 403 insufficient_scope with the missing scope in error.required_scope. GET /me works with any token.
| Scope | Allows | Endpoints |
|---|---|---|
| catalog:read | Read games, categories and attributes |
|
| listings:read | Read your listings |
|
| listings:write | Create, edit, pause and delete your listings (price, stock, photos) |
|
| orders:read | Read your sales and order details |
|
| orders:write | Start and deliver orders |
|
| messages:read | Read order chat messages |
|
| messages:write | Send order chat messages |
|
| wallet:read | Read your balance and ledger |
|
| webhooks:write | Manage your webhook endpoints |
|
| disputes:read | Read disputes on your orders (offers, evidence, timeline) |
|
| disputes:write | Answer disputes: accept, counter-offer, escalate and reply |
|
| notifications:read | Read your notifications |
|
| notifications:write | Mark your notifications as read |
|
| account:read | Read your account status, restrictions and break history |
|
| account:write | Go online/offline and start or end a break (vacation mode) |
|
| boosting:read | Read open boosting requests and your boosting offers |
|
| boosting:write | Send, update and withdraw boosting offers; post boost progress on orders |
|
Reading your webhook endpoints also needs webhooks:write: whoever can see an endpoint can also change it.
Sellers and buyers
- Everything that changes your store — listings, photos, units, bulk changes and CSV imports, orders (start, deliver, correct, cancel), dispute answers, online status and breaks — needs a verified seller account (
403 seller_not_verified) and a verified email address (403 email_not_verified). Finish both on the website first;GET /accountlists what is missing inrestrictions.reasons. - Payouts are read-only: request withdrawals on the website, where two-factor and tax checks apply.
- Everything goes through the same rules as the website: the same price limits, category attributes, photo checks, order states and escrow.
- You only ever see your own data. Another user’s listing, order or webhook returns
404 not_found, exactly like one that does not exist. - Buyers can use the API too:
GET /orders?role=buyerandGET /disputes?role=buyerlist your purchases and their disputes (read-only), and the order chat and notifications work for both sides.
Gift cards
A gift card offer is a normal listing of type giftcard, and its codes are its instant-delivery units. Each sale delivers one code automatically the moment the order is paid — you never deliver gift cards by hand.
- Pick the brand.
GET /giftcards/brands(scopecatalog:read) gives each brand’sgame_idandgame_category_id, the regions, face-value currencies and range it accepts, and the lowest live price.GET /giftcards/regionslists every region code. - Create the offer with its codes.
POST /listingswith the attributesregion,face_valueandface_currency, and the codes indelivery_units:The response has{ "game_id": 9, "game_category_id": 21, "title": "Steam Wallet $50 USD gift card", "price_cents": 4699, "status": "active", "attributes": { "region": "US", "face_value": 50, "face_currency": "USD" }, "delivery_units": [ { "code": "STM-4F7K-29QX-PL8M", "pin": "4412", "expires_at": "2028-12-31" }, { "code": "STM-8HJ2-77WD-XK3N" } ] }"type": "giftcard","stock": 2andinstant_delivery.enabled: true. - Add or remove codes.
POST /listings/{id}/unitswith{"units": [{"code": "…", "pin": "…"}]}adds codes (a code identical to one already on the listing — same code, PIN and expiry — is skipped);DELETE /listings/{id}/unitsremoves them. To replace every code in one call, sendPATCH /listings/{id}with"auto_delivery": trueand the newdelivery_units— withoutauto_delivery, a PATCH ignoresdelivery_units. - Know when it runs out. When the last code is sold the listing becomes
sold_outand your webhook receiveslisting.sold_out.
- Stock = number of codes (minus codes reserved by orders not delivered yet). You cannot set it:
stockin a PATCH is refused once the listing has codes, and bulk changes leave it alone with astock_managed_by_unitsnote. - A
codehas up to 255 characters, apinup to 64, andexpires_atis a date (YYYY-MM-DD) that has not passed. The same code twice in one request is stored once. At most 200 codes per listing. - Without codes a gift card can only be saved as a
draft. Warranty is always 0 days anddelivery_eta_minutesdefaults to 5. - Codes are stored encrypted and never come back through the API:
GET /listings/{id}/unitsshows masked previews (ST•••8M, PIN••••). Fix a typo in a code, its PIN or its expiry date withPATCH /listings/{id}/units/{unitId}(the unit gets a new id). - CSV import does not accept gift card categories (the file has no code column); create them with
POST /listings.
Pagination
Lists of listings, orders, disputes, ledger entries, payouts and webhook deliveries are paginated. Use per_page (1–100, default 25) and page, and follow links.next until it is null:
{
"data": [ … ],
"links": {
"first": "https://mythicdeals.com/api/v1/listings?page=1",
"last": "https://mythicdeals.com/api/v1/listings?page=4",
"prev": null,
"next": "https://mythicdeals.com/api/v1/listings?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 4,
"per_page": 25,
"to": 25,
"total": 92,
"path": "https://mythicdeals.com/api/v1/listings",
"links": [ … ]
}
}
- To sync, filter by
updated_since(listings, orders) and keep the newestupdated_atyou saw. - Order chat uses a cursor instead of pages: pass
since_idand continue withmeta.next_since_idwhilemeta.has_moreis true. - Games, categories, gift card brands and regions, images, units, dispute events and webhook endpoints are short lists and are not paginated. Notifications and competing offers take a
limitinstead of pages; your breaks use a shortermeta(current_page, per_page, total, last_page) withlinks.next/links.prev.
Errors
Every error has the same shape. Branch on error.code — it is stable — and show error.message to humans.
{
"error": {
"code": "insufficient_scope",
"message": "This token is missing the \"listings:read\" scope.",
"required_scope": "listings:read"
}
}
Validation errors (422) keep Laravel’s format, with messages per field:
{
"message": "Title is too short — at least 10 characters.",
"errors": {
"title": ["Title is too short — at least 10 characters."]
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_idempotency_key | The Idempotency-Key header is longer than 255 characters. |
| 401 | unauthenticated | Token missing, invalid, expired or revoked. |
| 403 | insufficient_scope | The token lacks the scope in required_scope. |
| 403 | seller_not_verified | Only verified sellers can do this. |
| 403 | email_not_verified | Verify your email address on the website first. |
| 403 | account_suspended | The account is suspended. |
| 403 | forbidden | A signed link (printable invoice) was changed or has expired. |
| 404 | not_found | It does not exist, or it is not yours. |
| 405 | method_not_allowed | Wrong HTTP method for this URL. |
| 409 | conflict | Not possible in the current state (e.g. delivering an unpaid order, editing a removed listing). |
| 409 | chat_closed | The order chat opens once the payment is complete. |
| 409 | preview_expired | The CSV import token was already used, has expired or belongs to another account. Upload the file again. |
| 409 | idempotency_conflict | The Idempotency-Key was already used with a different request. |
| 409 | idempotency_in_progress | A request with this key is still running. Retry in a moment. |
| 413 | payload_too_large | The upload is larger than the server accepts. |
| 422 | — | Validation failed (format above). |
| 422 | bulk_failed | An item of an atomic bulk update failed; nothing was saved. |
| 422 | too_many_matches | More listings match a bulk-update filter than one call may change (max). Narrow the filter. |
| 429 | rate_limited | Too many requests; wait Retry-After seconds. |
| 500 | server_error | Something went wrong on our side. Retry later. |
Rate limits
Each API token can make 120 requests per minute. Every response tells you where you stand:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
- Over the limit you get
429 rate_limitedwithRetry-After(seconds) andX-RateLimit-Reset(Unix time). Wait that long, then continue. - If several of your tools run from the same server, plan for them to share this budget.
- Use bulk endpoints and webhooks instead of polling: one
PATCH /listings/bulkupdates up to 100 listings, and onePOST /listings/bulk-updateup to 1000 listings that match a filter.
Idempotency
Networks fail. To retry a request that creates something without creating it twice, send an Idempotency-Key header with a unique value (a UUID is perfect, max 255 characters). These endpoints accept it:
POST /account/breaksPOST /disputes/{id}/counterPOST /disputes/{id}/messagesPOST /listingsPOST /listings/import/confirmPOST /listings/{id}/duplicatePOST /listings/{id}/imagesPOST /listings/{id}/unitsPOST /orders/{order}/cancelPOST /orders/{order}/deliverPOST /orders/{order}/messagesPOST /webhooks
- For 24 hours, the same key on the same endpoint returns the stored response instead of running again, with the header
Idempotent-Replayed: true. - The same key with a different body gets
409 idempotency_conflict. While the first request is still running, a second one gets409 idempotency_in_progress. - Keys belong to your account, so they never clash with other users’ keys.
- Responses are stored except validation errors (422) and server errors (5xx): after a 422, fix the request and retry with the same key.
POST /orders/{order}/start,POST /orders/{order}/deliver,POST /disputes/{id}/escalateandPUT /account/statusare safe to repeat anyway: an order is never started or delivered twice, and a dispute is escalated once.
Money, time and IDs
- Money is an integer number of US cents in fields ending in
_cents, always with"currency": "USD".1999= $19.99. Never use floats for money. - When creating or editing a listing you may instead send
price+currency(e.g. IDR, EUR); it is converted to USD cents at the current rate and the original is kept ininput_price. - Times are ISO-8601 in UTC, e.g.
2026-10-26T15:54:50Z. Filters such asupdated_sinceaccept any ISO-8601 time with an offset. - IDs are numbers. Orders also have a short
number(8 digits, shown as#48291735); every{order}in a URL accepts either. Instant-delivery units have string ids.
Versioning
- The version is in the path:
/api/v1. Within v1 we only make additive changes: new endpoints, new optional parameters, new response fields, new event types, new error codes. Write clients that ignore what they do not know. - Anything that could break a working integration — removing or renaming a field, changing a type or a default — goes into a new version (
/api/v2), announced in advance, with v1 kept running during a transition period. - Every change is listed in the changelog. The machine-readable contract is openapi.json (OpenAPI 3.1).
Questions or a bug in the API? Contact support and include the request method, URL, time and the error.code you received — never your token.