Developers

MythicDeals API

Manage listings, stock, prices, deliveries, chat, disputes and payouts from your own tools — and get signed webhooks the moment something happens.

On this page
  1. What you can automate
  2. Quick start
  3. Base URL & format
  4. Authentication
  5. Scopes
  6. Sellers and buyers
  7. Gift cards
  8. Pagination
  9. Errors
  10. Rate limits
  11. Idempotency
  12. Money, time and IDs
  13. Versioning

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.

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

  1. 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.
  2. 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"
  3. Build. Read the catalog (GET /games, then the category attributes) to create valid listings, sync your stock with PATCH /listings/bulk, and add a webhook for order.paid and dispute.created so you can deliver and answer without polling.

Base URL & format

All endpoints live under one base URL, over HTTPS only:

Base URL
https://mythicdeals.com/api/v1
  • Requests and responses are JSON. Send Accept: application/json and, with a body, Content-Type: application/json. Uploads (listing photos, delivery proofs, chat files) use multipart/form-data.
  • A single object comes back as {"data": {…}}, a list as {"data": […]} plus links and meta when it is paginated.
  • Unknown fields in your request are ignored. Empty values in responses are null rather than missing; a few fields only appear in some cases (for example a webhook secret right after it is created).

Authentication

Send your token in the Authorization header of every request:

HTTP
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 gets 403 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.

ScopeAllowsEndpoints
catalog:read Read games, categories and attributes
  • GET /categories/{id}/attributes
  • GET /games
  • GET /games/{slug}/categories
  • GET /giftcards/brands
  • GET /giftcards/regions
listings:read Read your listings
  • GET /listings
  • GET /listings/import/template
  • GET /listings/{id}
  • GET /listings/{id}/competitors
  • GET /listings/{id}/images
  • GET /listings/{id}/units
listings:write Create, edit, pause and delete your listings (price, stock, photos)
  • POST /listings
  • PATCH /listings/bulk
  • POST /listings/bulk-update
  • POST /listings/import/confirm
  • POST /listings/import/preview
  • PATCH /listings/{id}
  • DELETE /listings/{id}
  • POST /listings/{id}/activate
  • POST /listings/{id}/duplicate
  • POST /listings/{id}/images
  • PUT /listings/{id}/images/order
  • DELETE /listings/{id}/images/{imageId}
  • POST /listings/{id}/pause
  • POST /listings/{id}/units
  • DELETE /listings/{id}/units
  • PATCH /listings/{id}/units/{unitId}
  • DELETE /listings/{id}/units/{unitId}
orders:read Read your sales and order details
  • GET /orders
  • GET /orders/{order}
  • GET /orders/{order}/invoice
orders:write Start and deliver orders
  • POST /orders/{order}/cancel
  • POST /orders/{order}/deliver
  • PATCH /orders/{order}/delivery
  • POST /orders/{order}/start
messages:read Read order chat messages
  • GET /orders/{order}/messages
messages:write Send order chat messages
  • POST /orders/{order}/messages
wallet:read Read your balance and ledger
  • GET /payouts
  • GET /payouts/{id}
  • GET /wallet
  • GET /wallet/ledger
webhooks:write Manage your webhook endpoints
  • GET /webhooks
  • POST /webhooks
  • GET /webhooks/{webhook}
  • PATCH /webhooks/{webhook}
  • DELETE /webhooks/{webhook}
  • GET /webhooks/{webhook}/deliveries
  • POST /webhooks/{webhook}/deliveries/{delivery}/retry
  • POST /webhooks/{webhook}/test
disputes:read Read disputes on your orders (offers, evidence, timeline)
  • GET /disputes
  • GET /disputes/{id}
  • GET /disputes/{id}/events
  • GET /disputes/{id}/evidence/{evidenceId}
disputes:write Answer disputes: accept, counter-offer, escalate and reply
  • POST /disputes/{id}/accept
  • POST /disputes/{id}/counter
  • POST /disputes/{id}/escalate
  • POST /disputes/{id}/messages
notifications:read Read your notifications
  • GET /notifications
notifications:write Mark your notifications as read
  • POST /notifications/read
account:read Read your account status, restrictions and break history
  • GET /account
  • GET /account/breaks
account:write Go online/offline and start or end a break (vacation mode)
  • POST /account/breaks
  • DELETE /account/breaks/current
  • PUT /account/status
boosting:read Read open boosting requests and your boosting offers
  • GET /boosting/offers
  • GET /boosting/requests
  • GET /boosting/requests/{id}
boosting:write Send, update and withdraw boosting offers; post boost progress on orders
  • POST /boosting/offers/{id}/withdraw
  • POST /boosting/orders/{id}/progress
  • POST /boosting/requests/{id}/offer

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 /account lists what is missing in restrictions.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=buyer and GET /disputes?role=buyer list 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.

  1. Pick the brand. GET /giftcards/brands (scope catalog:read) gives each brand’s game_id and game_category_id, the regions, face-value currencies and range it accepts, and the lowest live price. GET /giftcards/regions lists every region code.
  2. Create the offer with its codes. POST /listings with the attributes region, face_value and face_currency, and the codes in delivery_units:
    POST /listings
    {
      "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"
        }
      ]
    }
    The response has "type": "giftcard", "stock": 2 and instant_delivery.enabled: true.
  3. Add or remove codes. POST /listings/{id}/units with {"units": [{"code": "…", "pin": "…"}]} adds codes (a code identical to one already on the listing — same code, PIN and expiry — is skipped); DELETE /listings/{id}/units removes them. To replace every code in one call, send PATCH /listings/{id} with "auto_delivery": true and the new delivery_units — without auto_delivery, a PATCH ignores delivery_units.
  4. Know when it runs out. When the last code is sold the listing becomes sold_out and your webhook receives listing.sold_out.
  • Stock = number of codes (minus codes reserved by orders not delivered yet). You cannot set it: stock in a PATCH is refused once the listing has codes, and bulk changes leave it alone with a stock_managed_by_units note.
  • A code has up to 255 characters, a pin up to 64, and expires_at is 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 and delivery_eta_minutes defaults to 5.
  • Codes are stored encrypted and never come back through the API: GET /listings/{id}/units shows masked previews (ST•••8M, PIN ••••). Fix a typo in a code, its PIN or its expiry date with PATCH /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:

JSON
{
  "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 newest updated_at you saw.
  • Order chat uses a cursor instead of pages: pass since_id and continue with meta.next_since_id while meta.has_more is 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 limit instead of pages; your breaks use a shorter meta (current_page, per_page, total, last_page) with links.next / links.prev.

Errors

Every error has the same shape. Branch on error.code — it is stable — and show error.message to humans.

JSON
{
  "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:

JSON
{
  "message": "Title is too short — at least 10 characters.",
  "errors": {
    "title": ["Title is too short — at least 10 characters."]
  }
}
StatusCodeMeaning
400invalid_idempotency_keyThe Idempotency-Key header is longer than 255 characters.
401unauthenticatedToken missing, invalid, expired or revoked.
403insufficient_scopeThe token lacks the scope in required_scope.
403seller_not_verifiedOnly verified sellers can do this.
403email_not_verifiedVerify your email address on the website first.
403account_suspendedThe account is suspended.
403forbiddenA signed link (printable invoice) was changed or has expired.
404not_foundIt does not exist, or it is not yours.
405method_not_allowedWrong HTTP method for this URL.
409conflictNot possible in the current state (e.g. delivering an unpaid order, editing a removed listing).
409chat_closedThe order chat opens once the payment is complete.
409preview_expiredThe CSV import token was already used, has expired or belongs to another account. Upload the file again.
409idempotency_conflictThe Idempotency-Key was already used with a different request.
409idempotency_in_progressA request with this key is still running. Retry in a moment.
413payload_too_largeThe upload is larger than the server accepts.
422—Validation failed (format above).
422bulk_failedAn item of an atomic bulk update failed; nothing was saved.
422too_many_matchesMore listings match a bulk-update filter than one call may change (max). Narrow the filter.
429rate_limitedToo many requests; wait Retry-After seconds.
500server_errorSomething 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:

HTTP
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
  • Over the limit you get 429 rate_limited with Retry-After (seconds) and X-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/bulk updates up to 100 listings, and one POST /listings/bulk-update up 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/breaks
  • POST /disputes/{id}/counter
  • POST /disputes/{id}/messages
  • POST /listings
  • POST /listings/import/confirm
  • POST /listings/{id}/duplicate
  • POST /listings/{id}/images
  • POST /listings/{id}/units
  • POST /orders/{order}/cancel
  • POST /orders/{order}/deliver
  • POST /orders/{order}/messages
  • POST /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 gets 409 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}/escalate and PUT /account/status are 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 in input_price.
  • Times are ISO-8601 in UTC, e.g. 2026-10-26T15:54:50Z. Filters such as updated_since accept 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.