API Documentation
SoftwareBay REST API v1 — Complete Reference for Developers
Overview
The SoftwareBay REST API enables business customers to programmatically access order data, license codes and download URLs. Typical use cases include ERP integration, automated license distribution, accounting exports and audit workflows.
Authentication
Every API request must pass the key in the Authorization header as a Bearer token:
Authorization: Bearer sb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Accept: application/json
Error Codes
All error responses contain an error field with a human-readable description.
{
"status": 401,
"error": "Unauthorized – invalid or inactive API key"
}
| HTTP Code | Meaning | Solution |
|---|---|---|
| 400 | Invalid request | Check parameters |
| 401 | Key invalid or revoked | Check key in account |
| 403 | Not a business account | Add company / VAT ID in profile |
| 404 | Order not found | Check order ID; does it belong to your account? |
| 429 | Rate limit exceeded | Wait – max. 60 requests/minute |
| 500 | Internal server error | Please contact support |
Rate Limiting
The limit is 60 requests per minute per API key. When exceeded, all further requests respond with 429 Too Many Requests. The window resets automatically after 60 seconds.
Endpoints
Returns basic information about the user of the API key. Suitable for connection testing and key verification.
Response Schema
| Field | Type | Description |
|---|---|---|
| user_id | integer | Internal user ID |
| string | Account email address | |
| name | string | First and last name |
| company | string | Company name |
| vat_id | string | VAT identification number |
| api_version | string | Current API version |
Example Response
{
"status": 200,
"data": {
"user_id": 1042,
"email": "firma@beispiel.de",
"name": "Max Mustermann",
"company": "Musterfirma GmbH",
"vat_id": "DE123456789",
"api_version": "1.0"
}
}Returns a paginated list of all orders for the account, sorted descending by order date.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | integer | Optional | Page number (default: 1) |
| per_page | integer | Optional | Entries per page, 1–50 (default: 20) |
| status | string | Optional | Filter by status — ausstehend | bezahlt | abgeschlossen | storniert |
Response Schema
| Field | Type | Description |
|---|---|---|
| orders[] | array | Order list (paginated) |
| orders[].id | integer | Internal order ID |
| orders[].bestellnummer | string | Human-readable order number |
| orders[].status | string | Order status |
| orders[].zahlungsstatus | string | Payment status |
| orders[].gesamtpreis_brutto | decimal | Total amount (gross) |
| orders[].waehrung | string | ISO 4217 (e.g. EUR) |
| orders[].created_at | datetime | Order timestamp (UTC) |
| pagination | object | Pagination data |
| pagination.page | integer | Current page |
| pagination.per_page | integer | Entries per page |
| pagination.total | integer | Total order count |
| pagination.pages | integer | Total page count |
Example Response
{
"status": 200,
"data": {
"orders": [
{
"id": 12345,
"bestellnummer": "S24DXG9F8JK2",
"status": "abgeschlossen",
"zahlungsstatus": "bezahlt",
"gesamtpreis_brutto": "29.99",
"waehrung": "EUR",
"created_at": "2026-04-18 10:23:45",
"lieferstatus": "vollstaendig"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 47,
"pages": 3
}
}
}Returns a complete order including all items, decrypted license codes and download URLs. License codes are only available for orders with status paid or completed.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | integer | Required | Internal order ID (from GET /orders) |
Response Schema (excerpt)
| Field | Type | Description |
|---|---|---|
| items[].license_codes[] | array | License codes for the order item |
| license_codes[].license_key | string | Decrypted license key |
| license_codes[].code_typ | string | Code type — retail | volumen | account | abo |
| license_codes[].download_url_64 | string|null | Download URL (64-bit) |
| license_codes[].download_url_32 | string|null | Download URL (32-bit) |
| license_codes[].aktivierungen_bestellung | integer | Number of allowed activations |
| billing_address | object | Billing address |
Example Response
{
"status": 200,
"data": {
"id": 12345,
"bestellnummer": "S24DXG9F8JK2",
"status": "abgeschlossen",
"gesamtpreis_brutto": "29.99",
"waehrung": "EUR",
"items": [
{
"id": 8821,
"product_name": "Microsoft Office 2021 Professional Plus",
"menge": 1,
"einzelpreis_brutto": "29.99",
"license_codes": [
{
"id": 5501,
"code_typ": "retail",
"license_key": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
"download_url_64": "https://download.microsoft.com/...",
"download_url_32": null,
"aktivierungen_bestellung": 1
}
]
}
]
}
}Code Examples
Basic API call
# Verify account
curl -X GET "https://softwarebay.de/api/v1/me" \
-H "Authorization: Bearer sb_live_YOUR_KEY"
# All orders (first page)
curl -X GET "https://softwarebay.de/api/v1/orders" \
-H "Authorization: Bearer sb_live_YOUR_KEY"
# Order #12345 with license codes
curl -X GET "https://softwarebay.de/api/v1/orders/12345" \
-H "Authorization: Bearer sb_live_YOUR_KEY"define('SB_API_KEY', 'sb_live_YOUR_KEY');
define('SB_API_URL', 'https://softwarebay.de/api/v1');
function sbApi($path) {
$ch = curl_init(SB_API_URL . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . SB_API_KEY,
'Accept: application/json',
],
]);
$json = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) throw new RuntimeException("API Error $status");
return json_decode($json, true)['data'];
}
$user = sbApi('/me');
$orders = sbApi('/orders');
$order = sbApi('/orders/12345');import requests
API_KEY = "sb_live_YOUR_KEY"
BASE = "https://softwarebay.de/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
def sb_api(path, params=None):
r = requests.get(BASE + path, headers=headers, params=params)
r.raise_for_status()
return r.json()["data"]
user = sb_api("/me")
orders = sb_api("/orders")
order = sb_api("/orders/12345")const API_KEY = 'sb_live_YOUR_KEY';
const BASE = 'https://softwarebay.de/api/v1';
async function sbApi(path) {
const r = await fetch(BASE + path, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
if (!r.ok) throw new Error(`API ${r.status}`);
return (await r.json()).data;
}
const user = await sbApi('/me');
const orders = await sbApi('/orders');
const order = await sbApi('/orders/12345');using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "sb_live_YOUR_KEY");
async Task<JsonElement> SbApi(string path) {
var json = await client.GetStringAsync($"https://softwarebay.de/api/v1{path}");
return JsonDocument.Parse(json).RootElement.GetProperty("data");
}
var user = await SbApi("/me");
var orders = await SbApi("/orders");
var order = await SbApi("/orders/12345");Fetch all orders paginated
// Collect all pages
$all = []; $page = 1;
do {
$result = sbApi("/orders?page={$page}&per_page=50");
$all = array_merge($all, $result['orders']);
$pages = $result['pagination']['pages'];
$page++;
usleep(200000); // 200ms delay – respect rate limit
} while ($page <= $pages);
echo count($all) . " orders loaded";import time
def all_orders():
orders, page = [], 1
while True:
data = sb_api("/orders", {"page": page, "per_page": 50})
orders.extend(data["orders"])
if page >= data["pagination"]["pages"]: break
page += 1
time.sleep(0.2) # respect rate limit
return orders
result = all_orders()
print(f"{len(result)} orders loaded")async function allOrders() {
const all = []; let page = 1;
do {
const data = await sbApi(`/orders?page=${page}&per_page=50`);
all.push(...data.orders);
if (page >= data.pagination.pages) break;
page++;
await new Promise(r => setTimeout(r, 200));
} while (true);
return all;
}Fetch completed orders only
curl "https://softwarebay.de/api/v1/orders?status=abgeschlossen&per_page=50" \
-H "Authorization: Bearer sb_live_YOUR_KEY"
Robust error handling
function sbApiSafe($path, $retry = 3) {
for ($i = 0; $i < $retry; $i++) {
$ch = curl_init(SB_API_URL . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . SB_API_KEY],
CURLOPT_TIMEOUT => 15,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status === 200) return json_decode($body, true)['data'];
if ($status === 429) { sleep(60); continue; }
$err = json_decode($body, true)['error'] ?? "HTTP $status";
throw new RuntimeException("API Error: $err");
}
throw new RuntimeException("Rate limit not resolved after $retry attempts");
}import time, requests
def sb_api_safe(path, params=None, retries=3):
for attempt in range(retries):
r = requests.get(BASE + path, headers=headers, params=params, timeout=15)
if r.status_code == 200: return r.json()["data"]
if r.status_code == 429:
time.sleep(60)
continue
r.raise_for_status()
raise RuntimeError("Rate limit not resolved")async function sbApiSafe(path, retries = 3) {
for (let i = 0; i < retries; i++) {
const r = await fetch(BASE + path, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
if (r.ok) return (await r.json()).data;
if (r.status === 429) { await new Promise(res => setTimeout(res, 60000)); continue; }
const err = (await r.json()).error ?? `HTTP ${r.status}`;
throw new Error(err);
}
}Bulk export: all license codes as CSV
// Export all completed orders → CSV with license codes
$fp = fopen('licenses.csv', 'w');
fputcsv($fp, ['Order#', 'Product', 'License Key', 'Type', 'Download']);
$page = 1;
do {
$list = sbApi("/orders?status=abgeschlossen&per_page=50&page={$page}");
foreach ($list['orders'] as $o) {
$order = sbApi("/orders/{$o['id']}");
foreach ($order['items'] as $item) {
foreach ($item['license_codes'] as $code) {
fputcsv($fp, [
$order['bestellnummer'],
$item['product_name'],
$code['license_key'],
$code['code_typ'],
$code['download_url_64'] ?? '',
]);
}
}
usleep(100000);
}
$page++;
} while ($page <= $list['pagination']['pages']);
fclose($fp);
echo "Export complete: licenses.csv";import csv, time
with open("licenses.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Order#", "Product", "License Key", "Type", "Download"])
page = 1
while True:
data = sb_api("/orders", {"status": "abgeschlossen", "per_page": 50, "page": page})
for o in data["orders"]:
order = sb_api(f"/orders/{o['id']}")
for item in order["items"]:
for code in item["license_codes"]:
writer.writerow([
order["bestellnummer"], item["product_name"],
code["license_key"], code["code_typ"],
code.get("download_url_64", ""),
])
time.sleep(0.1)
if page >= data["pagination"]["pages"]: break
page += 1
print("Export complete: licenses.csv")Live API Tester
Test your API key directly in the browser. The request runs through your own connection — the key is never sent to our servers.
Live API Tester
Manage API Keys
Keys are created in the customer account (max. 5 per account) and can be revoked at any time. A key that has been created cannot be restored.
Go to API section in customer account