buygolinks.com API
Branded short links with mobile deep linking and Amazon affiliate geo-routing. JSON over HTTPS.
https://app.buygolinks.com
Resolver: https://buygolinks.com/{code}
Auth: Bearer bgl_...
Overview
The buygolinks API lets you create and manage short links, set up per-store Amazon Associates tags, group links into collections, attach tracking pixels, and read aggregated click analytics.
Short links live at https://buygolinks.com/{code}. When a visitor clicks one, the resolver detects their country and device, logs the click, and either 302s them to the destination or — for Amazon links — picks the right local storefront, appends your Associates tag, and (on mobile) returns an interstitial that opens the Amazon app directly.
Authentication
Every /api/* endpoint requires Authorization: Bearer <token>. Two token shapes are accepted:
- Personal access token — format
bgl_<24 base62>. Recommended for any programmatic use. Create one viaPOST /api/api-keys. The raw token is returned exactly once; onlySHA-256(token)is stored. - Supabase JWT — issued to the SPA after login. Short-lived; not intended for backend integrations.
Quickstart
# Create a personal access token (Account Settings → API keys), then:
TOKEN="bgl_aB3xY7zQmN9pK2rT5sW8vCdE"
# List your links
curl -H "Authorization: Bearer $TOKEN" \
https://app.buygolinks.com/api/links
# Create a link
curl -X POST https://app.buygolinks.com/api/links \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"destination_url":"https://www.amazon.com/dp/B08N5WRWNW","source_label":"ig-bio"}'
# 14-day stats overview
curl -H "Authorization: Bearer $TOKEN" \
"https://app.buygolinks.com/api/stats/overview?days=14"
Errors
All error responses are JSON of shape { "error": "<code>" }. Common codes:
| HTTP | Codes |
|---|---|
| 400 | bad_json, invalid_url, url_required, tag_required, invalid_store, invalid_platform, invalid_pixel_id, invalid_alias, invalid_collection, name_required, no_changes |
| 401 | unauthorized |
| 403 | forbidden — resource exists but is not yours |
| 404 | not_found |
| 409 | alias_taken — rotator alias collision |
For AI agents
This site is built for programmatic consumption. Use whichever format your stack prefers:
OpenAPI 3.1
Full machine-readable spec. Import into Postman, OpenAI Custom GPT actions, or any OpenAPI tooling.
/openapi.yaml →llms.txt
Standard index file pointing at all docs. Fetch this first if you're an LLM doing discovery.
/llms.txt →llms-full.txt
Every endpoint in a single markdown document. Read once, answer anything.
/llms-full.txt →robots.txt
GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot all explicitly allowed.
/robots.txt →Links
List your links
Returns active links by default, plus per-link lifetime click counts and your current plan usage.
| Query | Type | Notes |
|---|---|---|
| include_archived | "1" | Include archived links in the response. |
{
"links": [{
"id": 42, "code": "aB3xY7zQ",
"destination_url": "https://www.amazon.com/dp/B08N5WRWNW",
"asin": "B08N5WRWNW", "title": "Echo Dot",
"source_label": "ig-bio",
"product_image_url": "https://.../products/B08N5WRWNW-aB3xY7zQ.jpg",
"collection_id": 3, "created_at": "2026-05-27T18:00:00Z",
"archived_at": null, "clicks": 128
}],
"short_domain": "buygolinks.com",
"usage": { "plan": "free", "links": {...}, "clicks_30d": {...} }
}
Create a short link
If destination_url is an Amazon /dp/{ASIN} URL, the server extracts the ASIN, best-effort scrapes the product image + title, and rehosts the image to our Storage bucket. Each scraping step is capped so a slow Amazon response never blocks link creation.
| Body | Type | Notes |
|---|---|---|
| destination_url | string (uri) | Required. http or https only. |
| title | string | Optional. Max 200 chars. |
| source_label | string | Optional. Becomes part of ascsubtag. Sanitized to [a-z0-9_-], max 60. |
{ "link": {...}, "short_url": "https://buygolinks.com/aB3xY7zQ", "short_domain": "buygolinks.com" }
Edit a link
Accepts any subset of title, source_label, archived. Empty patch returns 400 no_changes.
{ "title": "New title", "archived": true }
Delete a link
Hard delete. Cascades to link_clicks. Returns { "ok": true }.
Assign / detach collection
{ "collection_id": 3 } // or null to detach
Statistics
Time windows are clamped to 1–90 days (default 14). All times are UTC.
Account-wide aggregated stats
Single payload powering the Statistics page. Includes totals, period-over-period comparison, link performance health score (0–100), top countries/devices/browsers/referrers, by-day/by-hour/by-weekday buckets, top + growing links, and the 25 most recent clicks.
| Query | Type | Notes |
|---|---|---|
| days | integer | 1–90, default 14. |
Per-link aggregated stats
Same shape as /api/stats/overview minus account-wide rollups, plus by_language and per-click ip.
Amazon affiliate tags
The resolver looks up your tags and picks the best storefront per visitor. With geo_enabled=true, the visitor's country (from CF-IPCountry) is mapped to the matching Amazon store. Without geo, the resolver prefers your US tag, then falls back to any active tag, then to plain amazon.com with no tag.
https://{store_host}/dp/{ASIN}?tag={tag}&linkCode=ll1&ascsubtag=link-{code}-{store} — the ascsubtag shows up in Amazon Associates so you can attribute conversions back to a specific short link.List tags
Returns your tags plus the catalog of valid stores ({ "US": "www.amazon.com", "UK": "www.amazon.co.uk", ... }).
Create or update (upsert on owner+store)
| Body | Type | Notes |
|---|---|---|
| store | string | Required. ISO2 country, uppercased. Must be a key from the catalog. |
| tag | string | Required. Sanitized to [a-z0-9_-], max 60. |
| active | boolean | Default true. |
| geo_enabled | boolean | Default true. |
Update a tag
Accepts any subset of tag, active, geo_enabled.
Delete a tag
Collections
Group links into folders. With rotator_enabled=true, the public URL https://buygolinks.com/c/{rotator_alias} picks a random member of the collection on each visit.
List collections
With link + click counts.
Create
| Body | Type | Notes |
|---|---|---|
| name | string | Required. Max 80. |
| description | string | Max 280. |
| color | string | Hex from a fixed palette; invalid → #ef4444. |
| starred | boolean | |
| rotator_enabled | boolean | |
| rotator_alias | string | 8–24 chars [A-Za-z0-9_-]. Auto-generated if omitted while enabling rotator. |
Detail
Collection meta + member links + aggregated stats over the window (?days=1–90).
Update
Delete
Deletes the collection. Member links are detached (collection_id set to NULL), not deleted.
Tracking pixels
Meta / TikTok / Google pixels that fire on the deep-link interstitial page.
List pixels + platform catalog
The platforms object in the response tells you which platform keys are valid and gives a label/placeholder/help string for each.
Add
{ "platform": "meta", "pixel_id": "1234567890", "label": "Main pixel", "active": true }
Update
Delete
Custom domains
Serve your short links from your own hostname (e.g. go.acme.com) instead of buygolinks.com. SSL is auto-issued by Cloudflare for SaaS once the user adds the CNAME record. Free plan does not include custom domains; Small = 1, Medium = 3, Large = unlimited.
List the user's domains + plan tier + plan limit + CNAME target
Each row's status is force-refreshed against Cloudflare (recently-active rows are cached for 5 min).
Register a new branded hostname
// Request
{ "hostname": "go.acme.com" }
// Response
{
"domain": { "id": 7, "hostname": "go.acme.com", "status": "pending", ... },
"cname_target": "buygolinks.com",
"instructions": "Add a CNAME record at your DNS provider: go.acme.com CNAME buygolinks.com"
}
Errors: 402 upgrade_required, 402 plan_limit_reached, 400 invalid_hostname, 400 reserved_hostname, 409 hostname_taken, 502 cloudflare_error.
Force-refresh status from Cloudflare
Use after the user adds the CNAME to advance the row from pending to active.
Remove
Deletes both the Cloudflare hostname binding and the local record. Short links served via this hostname stop resolving immediately.
API keys
Personal access tokens for programmatic API access.
SHA-256(token) and cannot recover it. Lost a token? Create a new one and revoke the old.List your keys
Returns id, label, prefix (first 8 chars of the random portion, for UI identification), last_used_at, created_at, revoked_at. Token hash is never returned.
Create a new token
// Request
{ "label": "production server" }
// Response (token shown ONCE)
{
"key": { "id": 12, "label": "production server", "prefix": "aB3xY7zQ", ... },
"token": "bgl_aB3xY7zQmN9pK2rT5sW8vCdE"
}
Revoke
Existing usages start failing with 401 unauthorized on the next request.
Public short-link resolver
No auth. Logs a click and redirects.
Records country (from CF-IPCountry), device + browser (from User-Agent), referrer, language. Then:
- Amazon link (link has
asin): buildshttps://{store_host}/dp/{ASIN}?tag={tag}&linkCode=ll1&ascsubtag=link-{code}-{store}.- iOS: returns HTML that fires
com.amazon.mobile.shopping.web://{host}{path}, falls back to the full URL after 1.8s. - Android: returns HTML that fires
intent://{host}{path}#Intent;package=com.amazon.mshop.android.shopping;.... - Desktop: 302 directly.
- iOS: returns HTML that fires
- Non-Amazon link: 302 directly to
destination_url.
Collection rotator
No auth. Picks a random member of the collection identified by the rotator alias, then resolves it as above.