Examples#
Complete walkthroughs. Replace your-site.com with your own address. Keep the application key
from the website administrator in the API_KEY variable — every example sends it in the X-API-Key
header.
Signing in and a first read#
# 1. sign in — NOTE: form-encoded body
TOKEN=$(curl -s -X POST https://your-site.com/api/system/auth/login \
-H "X-API-Key: $API_KEY" \
-d "username=partner" -d "password=secret" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])")
# 2. list articles
curl -s "https://your-site.com/api/blog/article/get-all?limit=5&computeTotalCount=1" \
-H "X-API-Key: $API_KEY" \
-H "Authorization: Bearer $TOKEN"
Dereferencing the response#
import json, urllib.request
def unwrap(response):
"""Returns a list of full records from a response with _references."""
refs = response.get('_references', {})
def resolve(ref):
if not isinstance(ref, dict) or 'id' not in ref:
return ref
key = '-'.join(str(v) for v in ref['id'].values())
for cls in refs.values():
if key in cls:
return {k: resolve(v) for k, v in cls[key].items()}
return ref
return [resolve(item) for item in response.get('items', [])]
Dereferencing is recursive
Relations inside _references are references again.
A filtered listing#
Active products with "notebook" in the name, price descending:
/api/eshop/product-set/get-all
?filters[0][0]=equal&filters[0][1]=activated&filters[0][2]=1
&filters[1][0]=like&filters[1][1]=name&filters[1][2]=notebook&filters[1][matchType]=both
&orderBys[price]=0
&limit=20&computeTotalCount=1
&includes[]=translations
orderBys[price]=0 means descending
Zero is false. 1 is ascending.
Saving#
curl -s -X POST "https://your-site.com/api/bazaar/watchdog/save\
?data[email]=buyer@example.com\
&data[category][id]=12\
&data[active]=1" \
-H "X-API-Key: $API_KEY" \
-H "Authorization: Bearer $TOKEN"
The data goes in the URL, not the body
A POST with a JSON body returns "missing data".
Verifying the write:
curl -s "https://your-site.com/api/bazaar/watchdog/get?id=<id>&includes[]=category" \
-H "X-API-Key: $API_KEY" \
-H "Authorization: Bearer $TOKEN"
After writing with relations, always read back
The save response returns only flat values.
Refreshing the token#
RESPONSE=$(curl -s -X POST https://your-site.com/api/system/auth/refresh \
-H "X-API-Key: $API_KEY" \
-d "refreshToken=$REFRESH")
TOKEN=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])")
REFRESH=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['refreshToken'])")
Overwrite BOTH values
Keeping the old refresh token and using it signs you out entirely.
Error handling#
def handle(http_response, body):
if 'application/json' not in http_response.headers.get('Content-Type', ''):
raise RuntimeError('Server returned HTML — a 404 or 500')
if http_response.status == 429:
wait = int(http_response.headers.get('Retry-After', '1'))
raise RuntimeError(f'Rate limited; retry in {wait} s')
data = json.loads(body)
if http_response.status == 401:
if data.get('code') == 501:
raise RuntimeError('Application key missing or invalid')
raise RuntimeError('User token invalid or expired — refresh it')
if http_response.status == 403:
raise RuntimeError('Permission denied')
if 'success' in data: # a write
if not data['success']:
raise RuntimeError(f"Error {data.get('code')}: {data.get('message')}")
return data.get('data')
return unwrap(data) # a read
A call that sends the application key (and the token, if any) and passes the response to
handle:
import os, urllib.error, urllib.request
BASE = 'https://your-site.com'
API_KEY = os.environ['API_KEY']
def call(path, token=None):
headers = {'X-API-Key': API_KEY}
if token:
headers['Authorization'] = f'Bearer {token}'
request = urllib.request.Request(BASE + path, headers=headers)
try:
with urllib.request.urlopen(request) as http_response:
return handle(http_response, http_response.read())
except urllib.error.HTTPError as error: # 4xx and 5xx
return handle(error, error.read())
Pagination#
offset, limit, everything = 0, 100, []
while True:
d = call(f'/api/blog/article/get-all?offset={offset}&limit={limit}&computeTotalCount=1')
everything += unwrap(d)
if offset + limit >= d.get('totalCount', 0):
break
offset += limit
Without computeTotalCount=1 the total is zero and the loop ends at once