api
Pagination
Lists page with an opaque cursor: send it back with the same filters.
Updated · API v1
A page
{
"data": [ … ],
"has_more": true,
"next_cursor": "eyJ2IjoxLCJsIjoic2VhcmNoQ3JlYXRpdmVzIi…"
}- limit sets the page size (1 to 100, 20 by default; similar creatives up to 50).
- has_more is true when another page exists; next_cursor is then a string, else null.
- A page can hold fewer items than limit, even none, and still have more: Instagram items leave social pages after the page is read. Follow has_more, never the item count.
Cursors
- Send next_cursor as cursor with the same filters as the first request. limit may change between pages.
- A cursor is signed: it is bound to its list, its filters and your organization, and valid 24 hours. A changed, expired or foreign cursor answers 400 invalid_cursor.
- Do not parse or build cursors; their content may change without notice.
# First page
curl "https://www.adspy.sh/api/v1/creatives?query=protein&limit=50" \
-H "Authorization: Bearer $ADSPY_API_KEY"
# Next page: the same filters plus the cursor
curl "https://www.adspy.sh/api/v1/creatives?query=protein&limit=50&cursor=$NEXT_CURSOR" \
-H "Authorization: Bearer $ADSPY_API_KEY"async function* creatives(query) {
let cursor = null;
do {
const params = new URLSearchParams({ query, limit: "50" });
if (cursor) params.set("cursor", cursor);
const response = await fetch(`https://www.adspy.sh/api/v1/creatives?${params}`, {
headers: { Authorization: `Bearer ${process.env.ADSPY_API_KEY}` },
});
const page = await response.json();
if (!response.ok) throw new Error(`${page.code}: ${page.detail}`);
yield* page.data;
cursor = page.next_cursor;
} while (cursor);
}
for await (const creative of creatives("protein")) console.log(creative.id);def creatives(query):
cursor = None
while True:
params = {"query": query, "limit": 50}
if cursor:
params["cursor"] = cursor
response = requests.get(
"https://www.adspy.sh/api/v1/creatives",
params=params,
headers={"Authorization": f"Bearer {os.environ['ADSPY_API_KEY']}"},
timeout=70,
)
page = response.json()
if not response.ok:
raise RuntimeError(f"{page['code']}: {page['detail']}")
yield from page["data"]
cursor = page["next_cursor"]
if not cursor:
break
for creative in creatives("protein"):
print(creative["id"])Live lists
Pages read the library as it is when each page is requested, not a frozen snapshot. When the library changes between two pages, an item can appear twice or be skipped. For a complete, consistent copy, create an export.