How Requests Work PRO
Everything here applies to every route.
The basics
Every address starts with:
https://api.fastyvendor.com/api/v1| What | How it works |
|---|---|
| Format | JSON in UTF-8, with snake_case field names. Send Content-Type: application/json with a body. |
| Prices | Whole L$, as numbers: 299, never "299" or 299.00. |
| Dates | ISO 8601 in UTC: 2026-11-04T18:02:55.895Z. A date with no value is null. |
| Avatars | Their UUID (the key), like a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d. |
| Ids | Text with a prefix: prd_… for products, grp_… for groups, vd_… for vendors. Sales and gift cards use a UUID. Keep them as text. |
| New fields | We add fields, but never rename or remove them. Ignore the ones you don't know. |
Finding a product id
Search by name with GET /products?q=Riot and read id in the answer.
Long lists
Lists come in pages, with the next page's address in next_cursor:
{
"items": [ { "id": "prd_mxbjm9xtpchmet", "name": "Riot Boots" } ],
"next_cursor": "p:1791315729083183~prd_3catv3h84c677g"
}Pass it back as ?cursor= for the next page. null means you've reached the end. Choose the page size with ?limit=: the default is 50 and the maximum is 200.
const BASE = 'https://api.fastyvendor.com/api/v1'
const headers = { Authorization: `Bearer ${process.env.FASTY_KEY}` }
async function allProducts() {
const all = []
let cursor = null
do {
const url = new URL(`${BASE}/products`)
url.searchParams.set('limit', '200')
if (cursor) url.searchParams.set('cursor', cursor)
const page = await (await fetch(url, { headers })).json()
all.push(...page.items)
cursor = page.next_cursor
} while (cursor)
return all
}import os, requests
BASE = "https://api.fastyvendor.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['FASTY_KEY']}"}
def all_products():
items, cursor = [], None
while True:
params = {"limit": 200}
if cursor:
params["cursor"] = cursor
page = requests.get(f"{BASE}/products", params=params, headers=HEADERS).json()
items += page["items"]
cursor = page["next_cursor"]
if not cursor:
return itemsA cursor is only for the same list with the same filters. Treat it as text: don't try to read or build one.
Changing things
PATCH changes only the fields you send. Everything else stays as it is:
curl -X PATCH "https://api.fastyvendor.com/api/v1/products/prd_mxbjm9xtpchmet" \
-H "Authorization: Bearer $FASTY_KEY" \
-H "Content-Type: application/json" \
-d '{"price": 250}'The answer is the whole object, already changed. A field the route doesn't know is refused with 400 BAD_REQUEST, so a typo never passes silently.
Vendors pick up changes by themselves, usually within seconds.
Your tool can't send PATCH? Send a POST with the header X-HTTP-Method-Override: PATCH.
Safe retries
Say your script sends 10 points and the connection drops. Did Fasty get it? If you send again, the player could get 20.
The Idempotency-Key header solves this. Send any unique text with a POST and use the same text when you retry: Fasty does it once and gives you the first answer again.
curl -X POST "https://api.fastyvendor.com/api/v1/customers/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/points" \
-H "Authorization: Bearer $FASTY_KEY" \
-H "Idempotency-Key: round-42-a1b2c3d4" \
-H "Content-Type: application/json" \
-d '{"delta": 10, "note": "Won round 42"}'await fetch(`${BASE}/customers/${avatarKey}/points`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
'Idempotency-Key': `round-${round}-${avatarKey}`,
},
body: JSON.stringify({ delta: 10, note: `Won round ${round}` }),
})requests.post(
f"{BASE}/customers/{avatar_key}/points",
headers={**HEADERS, "Idempotency-Key": f"round-{round_id}-{avatar_key}"},
json={"delta": 10, "note": f"Won round {round_id}"},
)llHTTPRequest(API + "/customers/" + (string)winner + "/points",
[HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/json",
HTTP_CUSTOM_HEADER, "Authorization", "Bearer " + KEY,
HTTP_CUSTOM_HEADER, "Idempotency-Key", "round-" + (string)round + "-" + (string)winner],
"{\"delta\":10,\"note\":\"Won a round\"}");- Make the key mean the action, like
round-42-<avatar>. A random value made fresh on each attempt protects nothing. - Fasty remembers a key for 24 hours, and up to 255 characters of it.
- The same key with a different body is refused with
409 IDEMPOTENCY_MISMATCH, because it is probably a bug. - A failed attempt with a
5xxor429doesn't count, so you can retry with the same key.
Always use it to give points, gift cards and deliveries.
From a browser
The API doesn't answer JavaScript running in a web page, on purpose: anyone could read your key in the page source. Call it from your server, and let your page talk to your server.