Pagination
Lists come a page at a time. Products are numbered pages; the change feed uses cursors, so a page never shifts under you while new changes arrive.
Two ways to page
| Operation | Parameters |
|---|---|
GET /api/v1/products | page integer ≥ 1 · default 1page_size integer ≥ 1 ≤ 100 · default 50 |
GET /api/v1/events | after integer ≥ 0before integer ≥ 1limit integer ≥ 1 ≤ 500 · default 50 |
GET /api/v1/alerts/feed | before integer ≥ 1limit integer ≥ 1 ≤ 100 · default 50 |
GET /api/v1/alerts/{alert_id}/deliveries | limit integer ≥ 1 ≤ 200 · default 50 |
Numbered pages
GET /api/v1/products takes page (integer ≥ 1 · default 1) and page_size
(integer ≥ 1 ≤ 100 · default 50). Its answer says where you are:
pageintegerRequiredpage_sizeintegerRequiredpagesintegerRequiredtotalintegerRequiredProducts matching the filters, on every page.
The second page of 20
- cURL
- JavaScript
- Python
curl --request GET \
--url 'https://carryspec.com/api/v1/products?page=2&page_size=20' \
--header "Authorization: Bearer $CARRYSPEC_API_KEY"
const response = await fetch("https://carryspec.com/api/v1/products?page=2&page_size=20", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CARRYSPEC_API_KEY}`,
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${await response.text()}`);
}
console.log(JSON.stringify(await response.json(), null, 2));
import json
import os
import urllib.error
import urllib.request
request = urllib.request.Request(
"https://carryspec.com/api/v1/products?page=2&page_size=20",
method="GET",
headers={
"Authorization": "Bearer " + os.environ["CARRYSPEC_API_KEY"],
},
)
try:
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))
except urllib.error.HTTPError as error:
print(error.code, error.read().decode())
Products change between calls, so a product can move from one page to the next while you read. To
keep a copy in step, read what changed from GET /api/v1/events instead of reading every page again.
Paging back with a cursor
GET /api/v1/events answers newest first, limit at a time (integer ≥ 1 ≤ 500 · default 50). For the
page before, send next_before back as before:
next_beforeinteger or nullRequiredSend it as
beforefor the page of older events. Null: there are none, or you are following withafter.totalinteger or nullRequiredHow many events there are under these filters, on every page. Null while following with
after, when nothing is counted.
The page before event 48214
- cURL
- JavaScript
- Python
curl --request GET \
--url 'https://carryspec.com/api/v1/events?before=48214&limit=50' \
--header "Authorization: Bearer $CARRYSPEC_API_KEY"
const response = await fetch("https://carryspec.com/api/v1/events?before=48214&limit=50", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CARRYSPEC_API_KEY}`,
},
});
if (!response.ok) {
throw new Error(`${response.status}: ${await response.text()}`);
}
console.log(JSON.stringify(await response.json(), null, 2));
import json
import os
import urllib.error
import urllib.request
request = urllib.request.Request(
"https://carryspec.com/api/v1/events?before=48214&limit=50",
method="GET",
headers={
"Authorization": "Bearer " + os.environ["CARRYSPEC_API_KEY"],
},
)
try:
with urllib.request.urlopen(request) as response:
print(json.dumps(json.load(response), indent=2))
except urllib.error.HTTPError as error:
print(error.code, error.read().decode())
GET /api/v1/alerts/feed pages the same way, with its own next_before.
Following the feed
To get each change once, keep next_after and send it back as after: you get what was recorded
since, oldest first, and never the same event twice. Start from after=0 to read the history
from the beginning.
next_afterintegerRequiredThe newest event's id here (or the
afteryou sent, when nothing is new). Send it asafternext time for only what happened since.
Following the feed
- Python
- JavaScript
import os, time, requests
KEY = os.environ["CARRYSPEC_API_KEY"]
after = 0 # keep this somewhere that survives a restart
while True:
r = requests.get(
"https://carryspec.com/api/v1/events",
params={"after": after, "limit": 500},
headers={"Authorization": f"Bearer {KEY}"},
timeout=30,
)
r.raise_for_status()
page = r.json()
for event in page["events"]: # oldest first
print(event["id"], event["type"])
after = page["next_after"]
if len(page["events"]) < 500: # caught up: wait before asking again
time.sleep(15 * 60)
let after = 0; // keep this somewhere that survives a restart
async function readNew() {
const res = await fetch(`https://carryspec.com/api/v1/events?after=${after}&limit=500`, {
headers: { Authorization: `Bearer ${process.env.CARRYSPEC_API_KEY}` },
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
const { events, next_after } = await res.json();
for (const event of events) console.log(event.id, event.type); // oldest first
after = next_after;
return events.length;
}
after and before cannot be sent together: that is a 400 bad_request.
Most sources are read about every two hours, so asking every few minutes finds nothing new. Every 15 minutes keeps you current and uses fewer than 100 calls a day.
Writing code that lasts
- Ignore fields you do not know: new ones may be added to answers.
- Treat a
typeof event you do not handle as something to skip, not an error. - Store ids, not page numbers: an id stays the same for as long as the record exists.
- Read
error.codeto decide what to do, and showerror.messageto a person.