Keep a Copy of Your Sales PRO
You want your sales in your own database or spreadsheet. This script brings them all once, and after that, only what is new or changed.
You'll need: a key with sales:read.
How it works
Ask for sales in the order they last changed:
GET /sales?sort=updated_at&order=asc&limit=200The oldest change comes first. Keep the next_cursor of the last page. Next time, start from it: everything new or changed comes after it, including a sale that was delivered or refunded later.
Save the sales by id, replacing what you have. That way a sale that changed simply updates.
The last page still has a cursor
When you sort by updated_at, next_cursor is not null at the end, so you can come back later from where you stopped. You're done for now when a page has fewer items than you asked for.
The script
Run it every few minutes. It keeps its place in cursor.txt, and the sales in sales.json. Swap those for your own database.
import json, os, requests
BASE = "https://api.fastyvendor.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['FASTY_KEY']}"}
PAGE = 200
def read_file(name, default):
return open(name, encoding="utf-8").read() if os.path.exists(name) else default
cursor = read_file("cursor.txt", "")
sales = json.loads(read_file("sales.json", "{}"))
while True:
params = {"sort": "updated_at", "order": "asc", "limit": PAGE}
if cursor:
params["cursor"] = cursor
res = requests.get(f"{BASE}/sales", params=params, headers=HEADERS)
res.raise_for_status()
page = res.json()
for sale in page["items"]:
sales[sale["id"]] = sale # new, or a newer version of the same sale
cursor = page["next_cursor"] or cursor
if len(page["items"]) < PAGE:
break
json.dump(sales, open("sales.json", "w", encoding="utf-8"), ensure_ascii=False)
open("cursor.txt", "w", encoding="utf-8").write(cursor)
print(f"{len(sales)} sales saved")import { existsSync, readFileSync, writeFileSync } from 'node:fs'
const BASE = 'https://api.fastyvendor.com/api/v1'
const headers = { Authorization: `Bearer ${process.env.FASTY_KEY}` }
const PAGE = 200
const read = (file, fallback) => (existsSync(file) ? readFileSync(file, 'utf8') : fallback)
let cursor = read('cursor.txt', '')
const sales = JSON.parse(read('sales.json', '{}'))
while (true) {
const url = new URL(`${BASE}/sales`)
url.search = new URLSearchParams({ sort: 'updated_at', order: 'asc', limit: PAGE, ...(cursor && { cursor }) })
const res = await fetch(url, { headers })
if (!res.ok) throw new Error((await res.json()).error.message)
const page = await res.json()
for (const sale of page.items) sales[sale.id] = sale // new, or a newer version of the same sale
cursor = page.next_cursor ?? cursor
if (page.items.length < PAGE) break
}
writeFileSync('sales.json', JSON.stringify(sales))
writeFileSync('cursor.txt', cursor)
console.log(`${Object.keys(sales).length} sales saved`)Save the cursor after you save the sales. If the script stops halfway, the next run repeats a few sales, which does no harm.
What a sale looks like
The fields you'll use most:
| Field | Meaning |
|---|---|
id | The sale. Use it as your key. |
status | received, pending, delivered, needs_attention or refunded. |
amount | L$ paid. |
product.name | What was sold. |
buyer.name, buyer.key | Who paid. |
recipient | Who received it, when it was a gift. |
origin | Where it came from: vendor, gacha, marketplace, sent… |
created_at, updated_at | When it was sold and when it last changed. |
All of them are in the Sales reference.
API or webhooks?
| API | Webhooks | |
|---|---|---|
| How | You ask every few minutes | Fasty calls your server at each sale |
| Good for | Spreadsheets, reports, catching up | Reacting in seconds |
| Needs | A script that runs now and then | A server with a public address |
They work well together. The sale object is the same in both, so webhooks can react right away and this script can fill any gaps.