Open the panel

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=200

The 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.

python
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")
js
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:

FieldMeaning
idThe sale. Use it as your key.
statusreceived, pending, delivered, needs_attention or refunded.
amountL$ paid.
product.nameWhat was sold.
buyer.name, buyer.keyWho paid.
recipientWho received it, when it was a gift.
originWhere it came from: vendor, gacha, marketplace, sent…
created_at, updated_atWhen it was sold and when it last changed.

All of them are in the Sales reference.

API or webhooks? ​

APIWebhooks
HowYou ask every few minutesFasty calls your server at each sale
Good forSpreadsheets, reports, catching upReacting in seconds
NeedsA script that runs now and thenA 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.