General information
Introduction
Welcome to the 8keys API Docs! You can use our API to get information on your balance, products and orders.
In the dark area to the right you will find a sample implementation with PHP and Guzzle.
General Information about the API:
- Content-Type:
application/jsonorapplication/x-www-form-urlencoded(we strongly recommend using json) - Response Format:
JSON, make sure to addAccept: application/jsonto all your requests - Money Format:
Euroas float (recommended) orEuro-Centsas integer (e.g. 12972 for 129.72 EUR) - DateTime Format:
YYYY-MM-dd HH:mm:ss, e.g.2020-04-03 18:04:00(UTC timezone)
Environments:
| Name | Description |
|---|---|
| Sandbox | Environment to implement / test with virtual balance which can be topped up with one click. Orders are automatically deleted after 48 hours. |
| Production | Live environment for productive use with real balance which can be topped up via bank transfer / PayPal. |
Endpoints
Sandbox
https://api.8keys.de/sandbox/v1
Production
https://api.8keys.de/production/v1
Authentication
Generate API keys
To generate your API keys, go to My Merchants and click on
in the Actions column. Confirm the procedure and make sure to save your api keys! We recommend to frequently regenerate your API keys.

Authorization
Client implementation (used in the following code examples)
$api_client = new \GuzzleHttp\Client([
'base_uri' => 'https://api.8keys.de/sandbox/v1/',
'timeout' => 60.0,
'http_errors' => false,
'headers' => [
'Authorization' => 'Bearer EXAMPLE-API-KEY',
'Accept' => 'application/json',
'Content-Type' => 'application/json'
],
]);
The API key must be included in all API requests to the server in a header that looks like the following:
Authorization: Bearer EXAMPLE-API-KEY
Rate Limit
To protect our servers from too many requests, we have a rate limit of 5 requests per second per ip address. If you hit the rate limit, following requests will be blocked with 429 Too Many Requests.
Product-Key Types
We use MIME Types to describe the format of our product keys.
| Type | Notes | Example |
|---|---|---|
| text/plain | Product key as plain text | ABCD-1234-7890-4217 |
| image/jpeg image/png image/gif |
Product key as an image (base64 encoded) |
Error Codes
| Error Code | Description |
|---|---|
| 302 Found | You have probably forgotten the Accept: application/json header |
| 401 Unauthorized | API token is invalid or Merchant has not been activated yet. |
| 404 Not Found | Route or specified element not found. |
| 405 Method Not Allowed | You tried to access a resource with an invalid method. |
| 409 Duplicate | An order with the given external id already exists. |
| 422 Unprocessable Entity | Your request query / body data is invalid (see response body for more details). |
| 429 Too Many Requests | You're sending too many requests (see Rate Limit) |
| 500 Internal Server Error | We had a problem with our server. Try again later. |
Changelog
| Date | Notes |
|---|---|
| 11.08.2025 | Removed webhooks |
| 06.04.2024 | Increased rate limit to 5 requests per second per ip address |
| 23.08.2023 | Euro as decimal is now accepted at every endpoint (recommended). Euro cents as integer still works too. |
| 24.07.2023 | Added webhooks and 2 order status (canceling, refunding) |
| 26.10.2022 | Added Order Status + two order response examples |
| 25.10.2022 | Fixed typos |
| 25.10.2022 | Changed Order IDs from integer (e.g. 12624) to UUID (e.g. 61f501e1-3c47-4733-9fff-055678e73629) Please make sure your implementation saves our IDs as string/UUID Switched from Store to Merchant New sandbox/production endpoints Fixed Get Balance RouteMade descriptions simpler |
| 29.11.2021 | Added order external id and more descriptions |
| 07.06.2020 | Initial version |
Products
Get All Products
$api_client->get('products', [
'query' => [
'page' => 4,
'limit' => 50,
'name' => 'Win',
'productIds' => [ 1, 4, 7, 8 ],
'updatedSince' => '2000-04-03 00:00:00'
]
]);
The above command returns JSON structured like this:
// -> Status Code: 200 OK
{
"total_count": 350,
"result": [
{
"id": 1,
"name": "Anno 2070 Uplay CD Key",
"description": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy.",
"cover": "https:\/\/api.8keys.de\/example-cover.jpg",
"platform": "Uplay",
"language": "German, English, French, Spanish, Czech, Polish, Russian, Italian",
"region": "Region free",
"quantity": 4129,
"price": 6.15,
"price_cents": 615,
"updated_at": "2020-04-24 13:54:18"
},
[...]
]
}
Retrieve all products.
HTTP Request
GET /products
Query Parameters
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
| page | int | 1 | false | Current page |
| limit | int | 10 | false | Limit product count (between 1 and 100) |
| name | string | NULL | false | Filter by product name |
| productIds | array of int | NULL | false | Filter by product ids (max. 100) |
| updatedSince | datetime | NULL | false | Get products updated since datetime |
Get Specific Product
$api_client->get('products/1');
The above command returns JSON structured like this:
// -> Status Code: 200 OK
{
"result": {
"id": 1,
"name": "Anno 2070 Uplay CD Key",
"description": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy.",
"cover": "https:\/\/api.8keys.de\/example-cover.jpeg",
"platform": "Uplay",
"language": "German, English, French, Spanish, Czech, Polish, Russian, Italian",
"region": "Region free",
"quantity": 4129,
"price": 6.15,
"price_cents": 615,
"updated_at": "2020-04-24 13:54:18"
}
}
Retrieve a specific product.
HTTP Request
GET /products/{ID}
URL Parameters
| Parameter | Description |
|---|---|
| ID | The ID of the product to retrieve |
Orders
Get All Orders
$api_client->get('orders', [
'query' => [
'page' => 4,
'limit' => 50,
'createdAfter' => '2000-04-03 00:00:00',
'externalIds' => [
'be7795a3-0f38-45c9-b534-661aade8ec1c',
'test-external-id'
]
]
]);
The above command returns JSON structured like this:
// -> Status Code: 200 OK
{
"total_count": 12,
"result": [
{
"id": "aa3f8586-a41a-4564-b3d3-7ffc0dd51776",
"external_id": "2964836b-e787-488f-a5e8-cc8bbb65822f",
"status": "done",
"created_at": "2020-04-07 20:12:36"
},
[...]
]
}
Retrieves all orders (without product keys).
HTTP Request
GET /orders
Query Parameters
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
| page | int | 1 | false | Current page (>= 1) |
| limit | int | 10 | false | Limit order count (between 1 and 100) |
| createdAfter | datetime | NULL | false | Get orders created after datetime |
| externalIds | array of strings | NULL | false | Find orders with given external id(s) |
Get Specific Order
$api_client->get('orders/aa3f8586-a41a-4564-b3d3-7ffc0dd51776');
The above command returns JSON structured like this:
// -> Status Code: 200 OK
{
"id": "aa3f8586-a41a-4564-b3d3-7ffc0dd51776",
"external_id": "2964836b-e787-488f-a5e8-cc8bbb65822f",
"status": "processing",
"created_at": "2020-04-07 20:12:36"
}
// -> Status Code: 200 OK
{
"id": "aa3f8586-a41a-4564-b3d3-7ffc0dd51776",
"external_id": "2964836b-e787-488f-a5e8-cc8bbb65822f",
"status": "done", // or delivered
"created_at": "2020-04-07 20:12:36",
"product_keys": [
{
"product_id": 1,
"type": "text/plain",
"code": "0000-0000-0000-0000"
},
{
"product_id": 2,
"type": "image/jpeg",
"code": "\/9j\/4AAQSkZJRgABAQAASABIAAD\/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD\/wAALCAAoAWgBAREA\/8QAHAABAQEAAwEBAQAAAAAAAAAAAAgHBQYJBAEC\/8QAMRAAAQMEAgEDAwMEAQUAAAAAAgEDBAAFBgcIERIJEyEiMUEyYXEUFSNRF1NidIGi\/9oACAEBAAA\/APVOlKUpSlKUpSlKUpSlKUpSlKUpSlKUpSlKUpSlKVB\/qebt2Xj7OuOOGn79Is2SbXuyQX58V4mn246utMNsi4P1Ajrr6eRD0vi0Q\/YlRcS5EcIsh4Mazj8m+P8Au7M3MlxWZDPIUnvN+xObedBpTFtsU7D3TBCadV1FAl7L6fnbOcuzIm5vTDkbUiMDHHJoWPXFxgV7Rh450b3WkVfv4OeY9\/nxrsuuONuF8p\/T\/wBP6zzu7Xq22wLNaroj1odabf8AdaZMRHt1twfFUcLv6e\/hPmpk5V+nDxG4vabvGzb3sbY0maCf0dltx3CAi3C4Gi+018RO\/FOlM1T5QANU+ekXYvTN4tbE1fxqyzMxyJ3F8x2zBYkWg3YgPpaWGWn0gyDaNOjMikm6oL8eCtp8L5JXHMekrMz60OZLvzktm9+2JKFXluEN9HIcR9flERJAq66Ar9vEmfj4QRrmvSj2\/s7LrBszUWysmfyJNY3WLEt11lPE84rTpSQNn3S+o2wKL5B5KqojnXfigomfY7C2x6om0s1uZ7WvuE6HxC4raLdBspq27eHE7VDP58SNQ8XCJxDQEdbAQXsiqs+LvCLVvEu7X+663ybL7keRR2I8tu+TY74D7RESGHtMN9Evmvfff4+1YC76K3HB503S2VspFMlJepcD8r\/4tTjp3iTr61+pVaNX6aveQXXHtUux8gyK6XN5l00mxSFz2QVppsUH3zjMqKp5do8vfSdJ3nnTqbHd5+pnrfU+WzblEtGRYvDjSn7c423JbETuDiKBOAYovYJ9xX47r4+WfAuy8LtTSN\/cfd659ZrlYJ0Ns48y4tCclHnhaRGnIzbPRCpISiQkiiJ91bOLcl5+OcGrTye2ZCR65s4axd5jQojKTphAINInSdAj7pN9dJ0Pu\/CdJUX6F4vbh9ReySOQHJjduSW7GLrNfZsdis5IDZNtmoGbQH5NMNCaE2n0GZqBKRfZS\/jeOiN1emWlm3lx+3Df8gwVu4tQr3j16c8mh8+\/D3gDppxs+lD3BAHGyIPFV8u09N9WbCs22NcYzsrHUIbdk1rj3NgDXs2kdBCVsv8AuFVUV\/cVrtlKUpSlKUpSlKUpSlKUpSlKV5s890W3+oPxcvE9FGC7crdHAy\/Sjo3UO\/n9vdbVf\/VUd6kMuJD4TbRdmEKAcCI0Pf5cOdHEE\/nyVKk7PosuH6JlkZnIXuFHt7qd\/wDTO+oYf\/BDVocKZTEPh1qmZMfbYYZxGG466ZIINgLfakSr8IiIiqqrUb2FiV6m3MJ7J57Lr2htQP8AtQmXBVGbxJ8kVO0X7++QIZp19LDbYqgkfa+noiACgAKCIp0iInSIlR7zW5cX3Dp0fjRx0hu5JujMQ\/pGGYKof9iZcH5kOr9gd8FUhQlRAFPdNUFBQ+b4+cXG+JnFLKcQgSwuWYXSz3C63u5M99P3BYpIDbSr9Xtt9II99Kq+R9IpqiRD6eHEBvlRoq7psfamUWzArTksmPFxrHpTcVJNxWPGJ6XKIgNHP8ZMAAqPaeBKioir5arxaHM+JPPy48OIWeXfJtfZDa3J1qYub3uHCNIiygc6ToWzRGnWi8EETRQJRRUFEuPkpue2ce9I5Ztm4+2blkgEsFg1+JM5xUbjNf76J0gRevsPkv4qb\/So05dMU0tdN45qrsjLNt3E7zIkvp\/mKEJn7Kkv+3DN97tP1C63\/qsE5z4LkeyvU11tg+JbBuOD3i74xDZiZBbxMpEAkO4EpggONl2qCo\/Bj8Evz+K+Hlbwe37qbX\/\/ADVm\/I2ZviyYO81cpmN5n\/cQjk2po2piKTXPLrzRSRDbVRQvkv0rsfJfaEbkV6UFw2bieOt2SLLh2xXrPGRPahpDuzLDzTaIiJ7QEypD8J9Aj8JW4enJdrZd+FusXLQQEEa3vxXhFU7B9uU8LiKn4VSRV\/gkX81wPqnXe123hFnka5OgLtzftMSEBKnbj6XGO70P7oDThfwK12f077bcbVwu1XGugGDx2lySKGnS+y7JedaX+FbMFT9lSqOpSlKUpSlKUpSlKUpSlKUpSpf53cS7nykwCzP4TemrLn2FTiueOzXyIG1IvH3GCMUVW\/JW2iQ0ReiaH8Kq1NWfaV9TXlvarNpjfNrwvCMKiTGJN3vMCSwb1yRr7OG0y+6pl9yFtAZbU+lLrpOqM5j8eckyvhPN4+6KxhblLgRrLbrRbllMMKUeJJYVe3XjBvtG2lJVUkVVRfuq1ne3tNcrGvTtwXQWpMQVc1egW+xZVAC6w2XI9uFh1ZIC+bwtF5OCy2XgZeQOGiIqKqpkmh7L6qnHPW0DVutuKmumbTANx4npVzhOSZb7hdm88Y3UUM1+E76REERFERBRE3rD859TG\/aw2WudaYxDHsvYtLA4QNsmQySTNccUXlNSnvNorbaoY+4oipJ8+X6albQOm\/U747XbIsqxfjPiF9ynKZBP3PI8ivcCbcnkIvIm0dS5gggp\/WXQ9kXSkq+I+NpcW8z565Nmt1g8rtRYliuNBaicgSbRJjuuuzvdbRGyRua+viraur8iidinz+FweLxq5r8MNhZXM4ZWjFs315l8tZwY7e5INLbHVVek6cfY\/QK+CGDq+YCPmPYotd94kcSt3Qt8ZBy65W3S2OZ9eYxQ7bZ7c4LjVtbIBbUlIFUBUWgRoAAjTxIyIiJfj99QnRHIDk5lWt9R4Pirjes2Lk3dMrv39ziNI0ZGrSILBuo8assq8aILZIRPCifIr1Ztis1qxqyW\/G7HCbh221RWoUOO2nQMsNAgNgP7IIoifxXn5zJ0Py4uHNLEOSHHXVNuypvFcfix2HLjdIbMZZQuS0Ns2nJTLpIgPivYqidqnyvSpXA7Tw31XuVOLu6g2Jr\/AAHXeL3dxobpLhz2OnmgMTQTVuXKd8fIRXxAR8uulXpVSrS1TxtwfWnHG2capoLfMeas8i1XMnx8P65ZKuHKPpF7DzN5xRRFVRRURF+O6i7F+LvqA8Kr3eLTxOvOPbCwG6yymM2a9vMtGyap0hOA64ygueKCKmy6iH4ipCnSIOOWaVu71BuTTGgeWuwbfgTOFyXJLuHxIhRXJj7fw83G7Uxcd9vtUcN0\/FsiNoTFTr2EslmteOWeBjtkgswrba4zUOHGaToGGGwQG2xT8IIiiJ+yVyNKUpSlKUpSlKUpSlKUpSlKUpSlKUpSlKUpSlTjv7gxp\/kNsKxbXvN1yvE8vsCAjF5xaaxDkvK2SEyTpOMu9k2qfQSIJIi9KqogoNAwIr0KBFhyJ8me6w0DTkqQgI6+QiiK4aNiIIRKna+IiPar0iJ8V9tKUpSlKUpSlKUpSlKUpSlK\/9k="
}
]
}
// -> Status Code: 200 OK
{
"id": "aa3f8586-a41a-4564-b3d3-7ffc0dd51776",
"external_id": "2964836b-e787-488f-a5e8-cc8bbb65822f",
"status": "refunded", // or canceled
"created_at": "2020-04-07 20:12:36"
}
Retrieve a specific order.
HTTP Request
GET /orders/{ID}
URL Parameters
| Parameter | Description |
|---|---|
| ID | The ID of the order to retrieve |
Place Order
$api_client->post('orders', [
'json' => [
'products' => [
[
'product_id' => 5,
'quantity' => 1,
'price' => 2.99
],
[
'product_id' => 1,
'quantity' => 4,
'price_cents' => 615
],
[...]
],
'external_id' => 'EXTERNAL-ID',
]
]);
The above command returns JSON structured like this:
// -> Status Code: 201 Created
{
"order_id": "fd1a1f3d-cff4-4b26-bd8b-354b3a15e8fe"
}
// -> Status Code: 409 Duplicate
{
"order_id": "51e0080a-f1b3-4a8b-a63d-1e3942e213f3" // Id of already existing order
}
Places an order. Do not exceed a maximum of 50 units per order.
HTTP Request
POST /orders
Body Parameters
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
| products | array of products | - | true | Requested products |
| products.*.product_id | int | - | true | Product ID |
| products.*.quantity | int | - | true | Desired Quantity (max. 50) |
| products.*.price | float | - | see notes | Maximum price in euros |
| products.*.price_cents | int | - | see notes | Maximum price in cents |
| external_id | string (max. 64 chars) | NULL | false | strongly recommended External ID to avoid duplicate orders (because of connection problems or similar) |
Notes
- For pricing, either
products.*.priceorproducts.*.price_centsis required. Do not provide both.
Order Status
A new order placed via API / Frontend will have the status "processing".
GET /orders/{ID} will only return keys if order status is done or delivered.
| Status | Description |
|---|---|
| processing | Order is being processed by the system. |
| review | Order was selected for a manual review. |
| done | Order was successfully processed. Waiting for customer to fetch keys. |
| delivered | Order was successfully delivered to the customer. |
| canceling | Order will be canceled soon. |
| canceled | Order was canceled. Reason: Order could not be executed |
| refunding | Order will be (partially) refunded soon. |
| refunded | Order was (partially) refunded. Reason: Problem with key(s) reported by customer |
Merchant
Get Balance
$api_client->get('balance');
The above command returns JSON structured like this:
// -> Status Code: 200 OK
{
// 100.32 €
"balance": 100.32,
"balance_cents": 10032
}
Retrieve your current balance. Make sure to use the appropriate endpoint.
HTTP Request
GET /balance
Query Parameters
No parameters available.