ShipPulse
  • Pricing
  • Docs
  • Blog
  • Compare

Product

TestimonialsChangelogStatus PagesFeedbackRoadmapPricing

Resources

DocsBlogAPI ReferenceSDKHelp Center

Company

AboutContact

Legal

TermsPrivacyCookie PolicyDPASub-processors

Product updates

Changelog updates only. Unsubscribe any time.

ShipPulse operated by Igor Bogdanov, Limassol, Cyprus. [email protected]. Cyprus registration number pending — will be published once issued.

ShipPulse

© 2026 ShipPulse. All rights reserved.

OverviewQuick StartCore ConceptsWidget ReferencePlatform GuidesAPI ReferenceAPI PlaygroundError HandlingPaginationRate LimitingJavaScript SDKWebhooksZapier, n8n & MakeCustom DomainsTeam ManagementBilling & PlansNotification ChannelsAI FeaturesOverviewFrom SenjaFrom Testimonial.toFrom HeadwayFrom Canny

Introduction

  • Overview
  • Quick Start
  • Core Concepts

Embed Widgets

  • Widget Reference
  • Platform Guides

REST API

  • API Reference
  • API Playground
  • Error Handling
  • Pagination
  • Rate Limiting

SDK & Webhooks

  • JavaScript SDK
  • Webhooks
  • Zapier, n8n & Make

Guides

  • Custom Domains
  • Team Management
  • Billing & Plans
  • Notification Channels
  • AI Features

Migrations

  • Overview
  • From Senja
  • From Testimonial.to
  • From Headway
  • From Canny

Need help?

[email protected]
API

Pagination

The ShipPulse API supports offset-based pagination for list endpoints. All list responses include metadata to help you fetch subsequent pages.

Query parameters

ParameterTypeDefaultDescription
limitinteger20Number of results per page. Max 100.
offsetinteger0Number of results to skip before returning.
orderstringdescSort direction: asc or desc. Sorted by created_at.
cursorstring—Opaque cursor for cursor-based pagination (where supported).

Offset pagination

Most list endpoints use offset pagination. Pass limit and offset query parameters:

bash
# Page 1: first 20
GET /api/v1/testimonials?limit=20&offset=0

# Page 2: next 20
GET /api/v1/testimonials?limit=20&offset=20

# Page 3
GET /api/v1/testimonials?limit=20&offset=40

Response shape

json
{
  "data": [...],
  "meta": {
    "total": 143,
    "count": 20,
    "limit": 20,
    "offset": 0,
    "has_more": true
  }
}
FieldDescription
meta.totalTotal number of items matching the query (before pagination)
meta.countNumber of items in this response
meta.limitThe limit that was applied
meta.offsetThe offset that was applied
meta.has_moretrue if there are more results after this page

Cursor pagination (real-time feeds)

For real-time feeds like changelog entries or subscriber lists, cursor-based pagination is more reliable than offset — it avoids missing or duplicating items when new rows are inserted between requests.

bash
# First page
GET /api/v1/changelog?limit=20

# Response includes next_cursor when has_more is true
# { "meta": { "has_more": true, "next_cursor": "eyJpZCI6IjEyMyJ9" } }

# Next page: pass cursor
GET /api/v1/changelog?limit=20&cursor=eyJpZCI6IjEyMyJ9

Cursor response shape

json
{
  "data": [...],
  "meta": {
    "count": 20,
    "limit": 20,
    "has_more": true,
    "next_cursor": "eyJpZCI6IjEyMyJ9",
    "prev_cursor": null
  }
}

Cursors are opaque base64 strings. Do not parse or construct them — always use the value returned by the previous response.

Fetching all pages (JavaScript)

To fetch all results across multiple pages, iterate until has_more is false:

typescript
async function fetchAll<T>(
  baseUrl: string,
  apiKey: string,
  pageSize = 100,
): Promise<T[]> {
  const results: T[] = [];
  let offset = 0;

  while (true) {
    const url = `${baseUrl}?limit=${pageSize}&offset=${offset}`;
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    const { data, meta } = await res.json();

    results.push(...data);

    if (!meta.has_more) break;
    offset += pageSize;
  }

  return results;
}

// Example
const all = await fetchAll(
  "https://shippulse.dev/api/v1/testimonials",
  "sp_your_api_key",
);