Skip to main content
Version: 2026-05

List campaigns

GET /campaigns

Shape changed since 2025-10

The campaign list payload and response changed in 2026-05. If you're upgrading an existing integration, read Campaigns in the migration guide before you start.

Description

Lists the campaigns belonging to your tenant, newest first, as a cursor-paginated collection. A campaign is the gift catalogue offered on a send — it groups the products a recipient can be given, and it's the thing you reference from Create a gift request to drive what gets offered and how.

Each item in the list carries the full campaign shape — the same object Get a campaign returns, including its product, variant, stock-level, and warehouse details.

When to use

Use this endpoint to discover which campaigns exist — for example to present a picker, or to find the id you'll pass as campaign_id when you create a gift request. When you already know the campaign you want and only need that one, fetch it directly with Get a campaign rather than paging the whole list.

Parameters

Headers

AndOpen-API-Version (header) · string · required — the API version this request targets. Always send 2026-05. Requests authenticate with a bearer token; see Authentication for how to present it and Environments for the regional base URL to send it to.

Query parameters

All four are optional; sent together they page, sort, and filter the same collection.

limit (query) · integer · optional — how many campaigns to return per page. Defaults to 25, which is also the maximum: each campaign carries its full nested catalogue, so the page is capped to keep response size and time predictable. A larger value is clamped to 25 server-side rather than rejected; a non-numeric, zero, or negative value is rejected with a 400.

after (query) · string · optional — the keyset pagination cursor. Pagination is cursor-based, not page-numbered: to fetch the next page, pass the id of the last campaign in the page you just received. Omit it for the first page. A cursor that references no existing record is rejected with a 400. Stop paging when the response's has_more is false.

sort (query) · string · optional — the field to order by, - prefix for descending. Defaults to -created_at (newest first); created_at, name, and -name are also accepted. Any other field is rejected with a 400.

status (query) · string · optional — filter by public status. Accepts a single value (available or archived) or a comma-separated list of both. An unrecognised value matches nothing and yields an empty page rather than an error.

Worked examples

The cURL version is the raw HTTP call; the JavaScript, Python, and Ruby versions are idiomatic equivalents. Each requests the first page with the defaults.

curl -X GET "https://api.andopen.co/campaigns" \
-H "Authorization: Bearer <api_key>" \
-H "AndOpen-API-Version: 2026-05"
const response = await fetch("https://api.andopen.co/campaigns", {
headers: {
Authorization: "Bearer <api_key>",
"AndOpen-API-Version": "2026-05",
},
});

const page = await response.json();
for (const campaign of page.data) {
console.log(campaign.id, campaign.name, campaign.status);
}
import requests

response = requests.get(
"https://api.andopen.co/campaigns",
headers={
"Authorization": "Bearer <api_key>",
"AndOpen-API-Version": "2026-05",
},
)
response.raise_for_status()

page = response.json()
for campaign in page["data"]:
print(campaign["id"], campaign["name"], campaign["status"])
require "net/http"
require "json"

uri = URI("https://api.andopen.co/campaigns")

request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer <api_key>"
request["AndOpen-API-Version"] = "2026-05"

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.request(request)
end

page = JSON.parse(response.body)
page["data"].each do |campaign|
puts "#{campaign['id']} #{campaign['name']} #{campaign['status']}"
end

Paging through every campaign

To walk the whole collection, keep fetching while has_more is true, passing the last campaign's id as the after cursor each time.

async function listAllCampaigns() {
const campaigns = [];
let after;

do {
const url = new URL("https://api.andopen.co/campaigns");
if (after) url.searchParams.set("after", after);

const response = await fetch(url, {
headers: {
Authorization: "Bearer <api_key>",
"AndOpen-API-Version": "2026-05",
},
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const page = await response.json();

campaigns.push(...page.data);
const lastId = page.data.at(-1)?.id;
after = page.has_more && lastId ? lastId : undefined;
} while (after);

return campaigns;
}

Response shape

A successful call returns 200 OK with a page object: a data array of campaigns plus a boolean cursor flag. Paging is driven by has_more and the after cursor.

  • array · required — the campaigns in this page. Each entry is a full campaign object, identical to what Get a campaign returns; see that page for the per-field detail of the nested catalogue.
  • boolean · required — whether more campaigns exist beyond this page. When true, fetch the next page by passing the last item's id as the after cursor; when false, you've reached the end.
  • Each campaign's string · required is one of available, archived, and its array · required is the inline catalogue tree.
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Q2 Customer Appreciation",
"description": "Send a thank-you gift to our top accounts.",
"status": "available",
"archived_at": null,
"products": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567890",
"name": "Leather Notebook",
"enabled": true,
"variants": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-012345678901",
"name": "Medium / Black",
"sku": "NB-LTHR-MED-BLK",
"enabled": true,
"position": 1,
"stock_levels": [
{
"id": "d4e5f6a7-b8c9-0123-defa-123456789012",
"quantity": 42,
"warehouse": {
"id": "e5f6a7b8-c901-2345-efab-234567890123",
"name": "EU Central — Frankfurt",
"enabled": true,
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
},
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"has_more": true
}

Per-field notes

  • Every item is the full campaign. The list does not return a trimmed summary — each entry includes the same product, variant, stock-level, and warehouse details as the single-campaign endpoint. This is why the page size is capped at 25.
  • has_more drives paging, not a total count. The response carries no total and no next URL. Treat has_more: false as the only signal to stop, and build the next request from the last item's id.
  • status filtering is forgiving. An unknown filter value yields an empty page, not a 400 — so an empty data array can mean either "no matches" or "filter typo". The catalogue tree within each campaign follows the same warehouse-filtering rules described on Get a campaign.

Error cases

Every failure uses the shared error model — a top-level errors array of objects carrying type, code, message, and (for field-level problems) param. See Errors for the full type/code taxonomy and how to handle each category; the codes below are the ones this endpoint produces.

StatusMeaning
400The request is malformed or violates a precondition. Typical triggers are a missing or unsupported `AndOpen-API-Version` header, a request body that is not valid JSON, or pagination parameters that fail server-side validation (`limit` non-numeric or non-positive, `after` referencing a record that does not exist).
401Authentication failed — missing or invalid bearer token.
403Authorised but not permitted to access this resource.
429Too many requests — rate limit exceeded.
500Internal server error.
503Service unavailable — the tenant may be under maintenance. Check the `Retry-After` header.

Every error carries a type, one of: validation_error, authentication_error, authorization_error, not_found_error, rate_limit_error, api_error.

The ones you'll meet in practice:

  • 400 api_error — the request could not be processed: unsupported_api_version when the AndOpen-API-Version header is missing or unsupported (returned before authentication), or a rejected pagination parameter — a limit that is non-numeric, zero, or negative, an after cursor that references no existing record, or an unsupported sort field.
  • 401 authentication_error — the bearer token is missing, invalid, or expired (invalid_token).
  • 403 authorization_error — the token is valid but not permitted to list campaigns (forbidden).
  • 429 rate_limit_error — too many requests; back off and retry (rate_limited). The RateLimit-* response headers tell you the budget and reset time.