Skip to content
Floweet
Pro access

Cursor pagination

Lists answer { data, nextCursor, hasMore }: how to move from one page to the next, read a whole list, and what searches return.

api.floweet.com/v1 · Contract 1.0.0-beta.1 · Beta

Sections · Pagination

Lists (fuel stations, EV charging stations, prospects, simulations) are paginated with a cursor. Every page answers { data, nextCursor, hasMore }.

Parameters

  • limit: items per page, from 1 to 100 (20 by default).
  • cursor: the nextCursor of the previous page; omit it for the first page.

The page

GET/v1/fuel-stationsScope fuel-stations:read
Request
curl "https://api.floweet.com/v1/fuel-stations?fuels=B7%2CE85&country=FR" \
  -H "Authorization: Bearer $FLOWEET_API_KEY"
Response 200
{
  "data": [
    {
      "id": "28898708-4170-48bb-a3f5-e9b0d5d7ee53",
      "name": "Carrefour",
      "brand": "Carrefour",
      "city": "Rambouillet",
      "postalCode": "78120",
      "country": "FR",
      "fuels": [
        "B7",
        "GPL",
        "SP95",
        "SP95_E10",
        "SP98"
      ],
      "services": [],
      "open24h": true,
      "prices": {
        "B7": {
          "price": 2409,
          "updatedAt": "2026-10-24T20:51:00+00:00"
        },
        "GPL": {
          "price": 1102,
          "updatedAt": "2026-10-24T20:51:00+00:00"
        },
        "SP95": {
          "price": 2159,
          "updatedAt": "2026-10-24T20:51:00+00:00"
        },
        "SP98": {
          "price": 2225,
          "updatedAt": "2026-10-24T20:51:00+00:00"
        },
        "SP95_E10": {
          "price": 2119,
          "updatedAt": "2026-10-24T20:51:00+00:00"
        }
      }
    }
  ],
  "nextCursor": "eyJwIjoyLCJzIjoxfQ",
  "hasMore": true
}

hasMore is true while another page follows; then pass nextCursor as cursor. On the last page, nextCursor is null.

Rules

  • A cursor is opaque: do not decode it, do not build one. Its content may change without notice.
  • It carries the position and the page size: with a cursor, the page size is the cursor's, whatever limit says. Keep the same filters (q, fuels…) from one page to the next.
  • The cursor marks a position in the list, not an item: if items are added or removed while you read, an item may appear twice or be skipped. Deduplicate by id, and run a full pass again rather than keeping a cursor for later.
  • An unreadable cursor answers 400 invalid_cursor: start again from the first page, without cursor.
  • Searches are not paginated: the company search (limit from 1 to 100), the cheapest stations (10) and geocoding (5 at most) answer { results }.

Invalid cursor

Response 400
{
  "error": {
    "code": "invalid_cursor",
    "message": "This pagination cursor is invalid. Start the list again without “cursor”."
  }
}

Read a whole list

// Every station of a town, page by page (100 per page).
async function listAll(query) {
  const all = [];
  let cursor;
  do {
    const params = new URLSearchParams({ ...query, limit: "100", ...(cursor ? { cursor } : {}) });
    const res = await fetch(`https://api.floweet.com/v1/fuel-stations?${params}`, {
      headers: { Authorization: `Bearer ${process.env.FLOWEET_API_KEY}` },
    });
    if (!res.ok) throw new Error((await res.json()).error.code);
    const page = await res.json();
    all.push(...page.data);
    cursor = page.hasMore ? page.nextCursor : undefined;
  } while (cursor);
  // Deduplicate by id: a station added while reading may shift the pages.
  return [...new Map(all.map((s) => [s.id, s])).values()];
}

const stations = await listAll({ q: "Rambouillet" });

The same loop in Python

import os
import requests

def list_all(query):
    """Every station of a town, page by page (100 per page)."""
    items, cursor = [], None
    while True:
        params = {**query, "limit": 100, **({"cursor": cursor} if cursor else {})}
        res = requests.get(
            "https://api.floweet.com/v1/fuel-stations",
            params=params,
            headers={"Authorization": f"Bearer {os.environ['FLOWEET_API_KEY']}"},
            timeout=30,
        )
        res.raise_for_status()
        page = res.json()
        items.extend(page["data"])
        if not page["hasMore"]:
            break
        cursor = page["nextCursor"]
    # Deduplicate by id: a station added while reading may shift the pages.
    return list({s["id"]: s for s in items}.values())

stations = list_all({"q": "Rambouillet"})