Skip to main content
GET
/
v2
/
payment-links

List Payment Links

Returns a paginated list of payment links, ordered by creation date (most recent first). Supports filtering by status, customer, and date range.

Authentication

Authorization: Bearer glm_test_YOUR_API_KEY

Query Parameters

ParameterTypeDescription
limitintegerNumber of results per page (default: 10, max: 100)
starting_afterstringCursor for pagination (payment link ID)
statusenumFilter by status: active, paid, expired, cancelled
customer_emailstringFilter by customer email
payment_typeenumFilter by type: one_time, subscription
created_aftertimestampFilter links created after this date
created_beforetimestampFilter links created before this date

Request

GET /v2/payment-links?limit=10&status=paid

Response

{
  "data": [
    {
      "id": "plink_abc123",
      "url": "https://checkout.withgale.com/pay/plink_abc123",
      "amount_cents": 4995,
      "currency": "USD",
      "description": "Blood Pressure Monitor",
      "payment_type": "one_time",
      "status": "paid",
      "customer": {
        "email": "customer@example.com"
      },
      "order": {
        "id": "ord_xyz789"
      },
      "created_at": "2025-10-18T14:30:00Z",
      "paid_at": "2025-10-18T15:30:00Z"
    },
    {
      "id": "plink_def456",
      "url": "https://checkout.withgale.com/pay/plink_def456",
      "amount_cents": 9900,
      "currency": "USD",
      "description": "Monthly Membership",
      "payment_type": "subscription",
      "status": "paid",
      "customer": {
        "email": "member@example.com"
      },
      "subscription": {
        "id": "sub_abc123",
        "status": "active"
      },
      "created_at": "2025-10-17T10:00:00Z",
      "paid_at": "2025-10-17T10:15:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "plink_def456"
}

Response Fields

FieldTypeDescription
dataarrayArray of payment link objects
has_morebooleanWhether more results are available
next_cursorstringCursor for next page (use as starting_after)

Examples

curl https://api.withgale.com/v2/payment-links \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY"

Filter by Status

curl "https://api.withgale.com/v2/payment-links?status=paid&limit=50" \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY"

Filter by Customer

curl "https://api.withgale.com/v2/payment-links?customer_email=jane@example.com" \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY"

Pagination

const getAllPaidLinks = async () => {
  let allLinks = [];
  let hasMore = true;
  let cursor = null;

  while (hasMore) {
    const params = new URLSearchParams({
      status: 'paid',
      limit: '100',
      ...(cursor && { starting_after: cursor })
    });

    const response = await fetch(
      `https://api.withgale.com/v2/payment-links?${params}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.GALE_API_KEY}`
        }
      }
    );

    const data = await response.json();
    allLinks = allLinks.concat(data.data);
    hasMore = data.has_more;
    cursor = data.next_cursor;
  }

  return allLinks;
};

Filter by Date Range

// Get all payment links from last 30 days
const getRecentLinks = async () => {
  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

  const params = new URLSearchParams({
    created_after: thirtyDaysAgo.toISOString(),
    limit: '100'
  });

  const response = await fetch(
    `https://api.withgale.com/v2/payment-links?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  return await response.json();
};
curl "https://api.withgale.com/v2/payment-links?payment_type=subscription&status=paid" \
  -H "Authorization: Bearer glm_test_YOUR_API_KEY"

Use Cases

Revenue Dashboard

// Calculate total revenue from paid links
const calculateRevenue = async (startDate, endDate) => {
  const params = new URLSearchParams({
    status: 'paid',
    created_after: startDate.toISOString(),
    created_before: endDate.toISOString(),
    limit: '100'
  });

  const response = await fetch(
    `https://api.withgale.com/v2/payment-links?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  const { data } = await response.json();

  const totalRevenue = data.reduce((sum, link) => {
    return sum + link.amount_cents;
  }, 0);

  return {
    totalCents: totalRevenue,
    totalDollars: totalRevenue / 100,
    count: data.length
  };
};

Customer Payment History

// Show all payments for a customer
const getCustomerPayments = async (email) => {
  const params = new URLSearchParams({
    customer_email: email,
    status: 'paid',
    limit: '50'
  });

  const response = await fetch(
    `https://api.withgale.com/v2/payment-links?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  const { data } = await response.json();

  return data.map(link => ({
    description: link.description,
    amount: link.amount_cents / 100,
    paidAt: link.paid_at,
    orderId: link.order.id
  }));
};
// Find all expired links to follow up
const getExpiredLinks = async () => {
  const response = await fetch(
    'https://api.withgale.com/v2/payment-links?status=expired&limit=100',
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  const { data } = await response.json();

  return data.map(link => ({
    customerEmail: link.customer.email,
    amount: link.amount_cents / 100,
    description: link.description,
    expiredAt: link.expires_at
  }));
};
// List all active subscription payment links
const getActiveSubscriptions = async () => {
  const params = new URLSearchParams({
    payment_type: 'subscription',
    status: 'paid',
    limit: '100'
  });

  const response = await fetch(
    `https://api.withgale.com/v2/payment-links?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.GALE_API_KEY}`
      }
    }
  );

  const { data } = await response.json();

  return data.map(link => ({
    customerId: link.customer.email,
    subscriptionId: link.subscription.id,
    amount: link.amount_cents / 100,
    interval: link.subscription.interval,
    nextBilling: link.subscription.next_billing_date
  }));
};

Pagination Best Practices

Cursor-Based Pagination

Always use cursor-based pagination for large datasets:
// Good: Use cursor pagination
let cursor = null;
do {
  const params = new URLSearchParams({
    limit: '100',
    ...(cursor && { starting_after: cursor })
  });

  const response = await fetch(`/v2/payment-links?${params}`, {
    headers: { 'Authorization': `Bearer ${apiKey}` }
  });

  const data = await response.json();
  await processLinks(data.data);
  cursor = data.next_cursor;
} while (data.has_more);

// Bad: Don't use offset pagination
// Gale API doesn't support offset-based pagination

Errors

Status CodeError CodeDescription
400invalid_requestInvalid query parameters
401unauthorizedInvalid or missing API key
429rate_limit_exceededToo many requests
Example error:
{
  "error": {
    "code": "invalid_request",
    "message": "Invalid status value",
    "param": "status"
  }
}

Best Practices

Use Pagination

Always paginate large result sets using limit and cursors

Filter Wisely

Use filters to reduce data transfer and improve performance

Cache Results

Cache results when displaying historical data

Handle Empty Results

Check if data array is empty before processing