v1 — Stable

WaitList API

Built for AI agents and developers. Create lists, collect subscribers, and export data — all programmatically.

On this page

Quick Start

Two API calls is all it takes. Create a list from any website URL, then drop the embed code into your page.

1

Create a list

Analyze a website and generate a branded signup form in one call.

bash
curl -X POST https://waitlist.pl/api/v1/lists \ -H "Content-Type: application/json" \ -d '{"url":"https://yoursite.com","email":"[email protected]","auto_palette":true}'
2

Embed the form

Paste the returned embed snippet into any HTML page. Done.

html
<!-- WaitList --> <script src="https://waitlist.pl/v1/drop.js"></script> <div data-waitlist="emb_abc123..."></div>
Agent-friendly. Set auto_palette: true to skip palette selection — the API auto-picks the best match for the website's design.

Authentication

All authenticated endpoints use a Bearer token with the api_key returned when you create a list. Each list has its own unique key prefixed with sd_.

http header
Authorization: Bearer sd_k8x2mQ7nP3abYm7v...
Keep it secret. Your api_key grants full access to the list's subscriber data. Never expose it in client-side code or public repos. The embed_key (prefixed emb_) is safe to use in frontend code.

Unauthenticated requests to protected endpoints return 401 Unauthorized. If the key doesn't match the requested list, you'll get 403 Forbidden.

Endpoints

Base URL: https://waitlist.pl/api/v1

POST /api/v1/lists Create a new list. Analyzes the website, generates palettes, and returns embed code and API key.
Request body
json
{ "url": "https://example.com", "email": "[email protected]", "auto_palette": true, "palette_index": 0 // optional: 0=faithful, 1=refined, 2=bold }
Response 201
json
{ "id": "a1b2c3d4-...", "api_key": "sd_k8x2mQ7nP3abYm7v", "embed_key": "emb_abc123xyz", "embed_snippet": "<script src=...>...</script>\n<div data-waitlist=...></div>", "palettes": [/* 3 palette objects */], "selected_palette": 0, "tier": "free" }
Example
bash
curl -X POST https://waitlist.pl/api/v1/lists \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com","email":"[email protected]","auto_palette":true}'
POST /api/v1/analyze Preview palettes for a website without creating a list. Useful for letting users choose a palette before committing.
Request body
json
{ "url": "https://example.com" }
Response 200
json
{ "tokens": { "colorRules": [/* extracted CSS */], "fonts": ["Inter", "Roboto"], "logo": "https://...", "title": "Site Name", "screenshot": { "success": true } }, "palettes": [ { "name": "Faithful", "description": "Mirrors the site's palette", "colors": { "emailBg": "#f1f3f4", "cardBg": "#ffffff", "headerBg": "#1a73e8", "headerText": "#ffffff", "bodyText": "#202124", "mutedText": "#5f6368", "buttonBg": "#ea4335", "buttonText": "#ffffff", "accent": "#1a73e8", "border": "#e8eaed", "footerBg": "#f8f9fa", "footerText": "#9aa0a6" }, "suggestedFont": "'Inter', sans-serif" } // ... Refined, Bold ] }
Example
bash
curl -X POST https://waitlist.pl/api/v1/analyze \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com"}'
POST /api/v1/lists/:embed_key/subscribe Add a subscriber to a list. Sends a double opt-in confirmation email automatically.
Request body
json
{ "email": "[email protected]", "turnstile_token": "xxx" // optional — auto-included by embed widget }
Response 200
json
{ "status": "pending", "message": "Confirmation email sent" }
Example
bash
curl -X POST https://waitlist.pl/api/v1/lists/emb_abc123xyz/subscribe \ -H "Content-Type: application/json" \ -d '{"email":"[email protected]"}'
Possible statuses: pending (confirmation email sent), confirmed (already confirmed), already_subscribed (duplicate).
GET /api/v1/embed/:embedKey/style Get brand palette colors for an embed widget. Public, no auth required. Cached for 1 hour.
Response 200
{ "buttonBg": "#0984e3", "buttonText": "#ffffff", "accent": "#0984e3", "headerBg": "#2c3e50", "border": "#dfe6e9", "font": "'Inter', sans-serif" }
Example
curl https://waitlist.pl/api/v1/embed/emb_abc123xyz/style
Used by drop.js to automatically apply brand colors. CORS enabled for cross-origin requests.
GET /api/v1/lists/:id Bearer token Get list info and stats. Returns current tier, subscriber counts, and limit status.
Response 200
json
{ "id": "a1b2c3d4-...", "website_url": "https://example.com", "tier": "free", "stats": { "total_subscribers": 142, "confirmed": 89, "pending": 53, "bounce_rate": "2.1%" }, "limits": { "max_confirmed": 100, "can_access_subscribers": true }, "suspended": false }
Example
bash
curl https://waitlist.pl/api/v1/lists/a1b2c3d4-... \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v"
GET /api/v1/lists/:id/subscribers Bearer token Fetch subscribers from a list. Paginated, filterable by status and date.
Query parameters
params
status=confirmed // default: confirmed since=2026-01-01 // incremental sync page=1 per_page=100 // max: 100
Response 200
json
{ "subscribers": [ { "email": "[email protected]", "confirmed_at": "2026-01-15T10:30:00Z" } ], "total": 47, "page": 1, "has_more": false }
Example
bash
curl "https://waitlist.pl/api/v1/lists/a1b2c3d4-.../subscribers?status=confirmed&per_page=50" \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v"
402 Payment Requiredreturned when a free-tier list exceeds 100 confirmed subscribers. Upgrade via the /pay endpoint to access subscriber data.
DELETE /api/v1/lists/:id/subscribers/:email Bearer token Permanently delete a subscriber. For GDPR right-to-erasure compliance.
URL parameters
params
:id // list UUID :email // subscriber email (URL-encoded)
Response 200
json
{ "deleted": true, "email": "[email protected]" }
Example
bash
curl -X DELETE https://waitlist.pl/api/v1/lists/a1b2c3d4-.../subscribers/user%40example.com \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v"
POST /api/v1/lists/:id/pay Bearer token Initiate a Stripe Checkout session to upgrade a list's tier.
Request body
json
{ "tier": "unlock_1k" // unlock_1k | unlock_10k | unlimited }
Response 200
json
{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_live_..." }
Example
bash
curl -X POST https://waitlist.pl/api/v1/lists/a1b2c3d4-.../pay \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v" \ -H "Content-Type: application/json" \ -d '{"tier":"unlock_1k"}'
GET /api/v1/lists/:id/transactions Bearer token Get payment transaction history for a list. For accounting, bookkeeping, and receipt access.
Response 200
json
{ "transactions": [ { "id": "a1b2c3d4-...", "tier": "unlock_1k", "amount": { "cents": 500, "currency": "usd", "formatted": "$5.00" }, "payment_type": "one_time", "status": "completed", "description": "WaitList unlock 1k", "invoice_url": "https://invoice.stripe.com/...", "receipt_url": "https://receipt.stripe.com/...", "created_at": "2026-01-15T10:30:00Z" } ], "total": 1 }
Example
bash
curl https://waitlist.pl/api/v1/lists/a1b2c3d4-.../transactions \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v"
Accounting-ready. Each transaction includes Stripe receipt and invoice URLs. Use invoice_url to download PDF invoices for bookkeeping. Refunds appear as negative amounts with status: "refunded".

Rate Limits

Rate limits are enforced per scope. Exceeding them returns 429 Too Many Requests with a Retry-After header.

Scope Limit Window
Per email address (global) 3 confirmation emails per day
Per list (subscribes) 50 requests per minute
Per list (subscribes) 500 requests per hour
Per list (pending cap) 1,000 unconfirmed at any time
Per IP address 10 subscribes per minute
API key (subscriber fetch) 100 requests per minute
Bounce protection. Lists with a bounce rate above 30% (with 10+ emails sent) are automatically suspended. Lists above 10% (with 50+ sent) trigger a soft suspension with owner notification.

Code Examples

Full working examples in curl, Node.js, and Python. Copy, paste, run.

# 1. Create a list curl -X POST https://waitlist.pl/api/v1/lists \ -H "Content-Type: application/json" \ -d '{ "url": "https://mysite.com", "email": "[email protected]", "auto_palette": true }' # Response includes api_key and embed_key # Save the api_key: sd_k8x2mQ7nP3abYm7v # 2. Fetch subscribers curl https://waitlist.pl/api/v1/lists/LIST_ID/subscribers \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v" # 3. Delete a subscriber (GDPR) curl -X DELETE https://waitlist.pl/api/v1/lists/LIST_ID/subscribers/user%40example.com \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v" # 4. Upgrade to 1k tier curl -X POST https://waitlist.pl/api/v1/lists/LIST_ID/pay \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v" \ -H "Content-Type: application/json" \ -d '{"tier": "unlock_1k"}'
// WaitList API — Node.js example const API_KEY = 'sd_k8x2mQ7nP3abYm7v'; const BASE = 'https://waitlist.pl/api/v1'; // 1. Create a list const res = await fetch(`${BASE}/lists`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://mysite.com', email: '[email protected]', auto_palette: true }) }); const list = await res.json(); console.log('Embed:', list.embed_snippet); console.log('API Key:', list.api_key); // 2. Fetch subscribers const subs = await fetch( `${BASE}/lists/${list.id}/subscribers`, { headers: { 'Authorization': `Bearer ${list.api_key}` } } ).then(r => r.json()); console.log(`${subs.total} subscribers`); subs.subscribers.forEach(s => console.log(` ${s.email} — ${s.confirmed_at}`) );
# WaitList API — Python example import requests API_KEY = "sd_k8x2mQ7nP3abYm7v" BASE = "https://waitlist.pl/api/v1" # 1. Create a list res = requests.post(f"{BASE}/lists", json={ "url": "https://mysite.com", "email": "[email protected]", "auto_palette": True }) data = res.json() print("Embed:", data["embed_snippet"]) print("API Key:", data["api_key"]) # 2. Fetch subscribers headers = {"Authorization": f"Bearer {data['api_key']}"} subs = requests.get( f"{BASE}/lists/{data['id']}/subscribers", headers=headers ).json() print(f"{subs['total']} subscribers") for s in subs["subscribers"]: print(f" {s['email']}{s['confirmed_at']}") # 3. Delete a subscriber (GDPR) requests.delete( f"{BASE}/lists/{data['id']}/subscribers/[email protected]", headers=headers )

Pricing

Start free. Pay per-list only when you need more subscribers. No accounts, no monthly bills unless you choose unlimited.

Free
$0
forever
  • Up to 100 confirmed subscribers
  • Full API access
  • AI-generated email template
  • Double opt-in emails
10K
$19.99
one-time, per list
  • Up to 10,000 confirmed subscribers
  • Full API access
  • CSV export
  • Everything in Free
Unlimited
$10/mo
subscription, per list
  • Unlimited confirmed subscribers
  • Full API access
  • Priority support
  • Everything in Free
How it works. Subscribers are always collected, even on the free tier. When you exceed 100 confirmed subscribers, API access to subscriber data returns 402 Payment Required until you upgrade. New subscribers still receive confirmation emails — you never lose signups.

Agent Payments

AI agents can upgrade lists programmatically using the pay endpoint. The flow returns a Stripe Checkout URL that can be opened in a browser or processed automatically.

Payment Flow

The agent calls the pay endpoint with the desired tier, receives a checkout URL, and either redirects the user or opens a browser window to complete payment.

1

Request checkout

Call POST /api/v1/lists/:id/pay with the desired tier. Returns a Stripe checkout_url.

2

Complete payment

Redirect the user to the checkout_url or open it in a browser. Stripe handles the rest.

Agent Example

A fully automated agent can initiate payment and poll for tier changes after the user completes checkout.

bash
# 1. Request a checkout session curl -X POST https://waitlist.pl/api/v1/lists/LIST_ID/pay \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v" \ -H "Content-Type: application/json" \ -d '{"tier":"unlock_1k"}' # Response: {"checkout_url": "https://checkout.stripe.com/c/pay/cs_live_..."} # 2. Open the checkout URL in a browser (agent-side) # - On macOS: open "$CHECKOUT_URL" # - On Linux: xdg-open "$CHECKOUT_URL" # - Or return the URL to the user for manual completion # 3. Poll for tier upgrade confirmation curl https://waitlist.pl/api/v1/lists/LIST_ID \ -H "Authorization: Bearer sd_k8x2mQ7nP3abYm7v" # Check response: "tier" changes from "free" to "unlock_1k" after payment
Fully automated. After calling /pay, the agent receives a Stripe Checkout URL. For human-in-the-loop flows, redirect the user. For fully automated agents, use Stripe's API directly with the checkout session ID to complete payment programmatically.
Tier verification. Always poll GET /api/v1/lists/:id after payment to confirm the tier upgrade. Stripe webhooks process asynchronously, so the tier may take a few seconds to update.

Error Codes

All error responses follow the same shape:

json
{ "error": "rate_limit_exceeded", "message": "Too many requests. Retry after 60 seconds." }
Status Code Description
400 invalid_request Missing or malformed request body / parameters
401 unauthorized Missing or invalid Authorization header
402 payment_required Free tier limit exceeded — upgrade needed
403 forbidden API key does not match the requested list
404 not_found List or subscriber not found
422 invalid_email Email failed syntax or MX validation
429 rate_limit_exceeded Too many requests — check Retry-After header
503 list_suspended List suspended due to high bounce rate