buygolinks.com API

Branded short links with mobile deep linking and Amazon affiliate geo-routing. JSON over HTTPS.

API: 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:

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:

HTTPCodes
400bad_json, invalid_url, url_required, tag_required, invalid_store, invalid_platform, invalid_pixel_id, invalid_alias, invalid_collection, name_required, no_changes
401unauthorized
403forbidden — resource exists but is not yours
404not_found
409alias_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 →

GET/api/links

List your links

Returns active links by default, plus per-link lifetime click counts and your current plan usage.

QueryTypeNotes
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": {...} }
}
POST/api/links

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.

BodyTypeNotes
destination_urlstring (uri)Required. http or https only.
titlestringOptional. Max 200 chars.
source_labelstringOptional. Becomes part of ascsubtag. Sanitized to [a-z0-9_-], max 60.
{ "link": {...}, "short_url": "https://buygolinks.com/aB3xY7zQ", "short_domain": "buygolinks.com" }
PATCH/api/links/{id}

Edit a link

Accepts any subset of title, source_label, archived. Empty patch returns 400 no_changes.

{ "title": "New title", "archived": true }
DELETE/api/links/{id}

Delete a link

Hard delete. Cascades to link_clicks. Returns { "ok": true }.

POST/api/links/{id}/collection

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.

GET/api/stats/overview

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.

QueryTypeNotes
daysinteger1–90, default 14.
GET/api/stats/link/{id}

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.

The link is built as 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.
GET/api/amazon-tags

List tags

Returns your tags plus the catalog of valid stores ({ "US": "www.amazon.com", "UK": "www.amazon.co.uk", ... }).

POST/api/amazon-tags

Create or update (upsert on owner+store)

BodyTypeNotes
storestringRequired. ISO2 country, uppercased. Must be a key from the catalog.
tagstringRequired. Sanitized to [a-z0-9_-], max 60.
activebooleanDefault true.
geo_enabledbooleanDefault true.
PATCH/api/amazon-tags/{id}

Update a tag

Accepts any subset of tag, active, geo_enabled.

DELETE/api/amazon-tags/{id}

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.

GET/api/collections

List collections

With link + click counts.

POST/api/collections

Create

BodyTypeNotes
namestringRequired. Max 80.
descriptionstringMax 280.
colorstringHex from a fixed palette; invalid → #ef4444.
starredboolean
rotator_enabledboolean
rotator_aliasstring8–24 chars [A-Za-z0-9_-]. Auto-generated if omitted while enabling rotator.
GET/api/collections/{id}

Detail

Collection meta + member links + aggregated stats over the window (?days=1–90).

PATCH/api/collections/{id}

Update

DELETE/api/collections/{id}

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.

GET/api/tracking-pixels

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.

POST/api/tracking-pixels

Add

{ "platform": "meta", "pixel_id": "1234567890", "label": "Main pixel", "active": true }
PATCH/api/tracking-pixels/{id}

Update

DELETE/api/tracking-pixels/{id}

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.

GET/api/domains

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).

POST/api/domains

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.

POST/api/domains/{id}/verify

Force-refresh status from Cloudflare

Use after the user adds the CNAME to advance the row from pending to active.

DELETE/api/domains/{id}

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.

The raw token is returned exactly once at creation. Save it immediately — we store only SHA-256(token) and cannot recover it. Lost a token? Create a new one and revoke the old.
GET/api/api-keys

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.

POST/api/api-keys

Create a new token

// Request
{ "label": "production server" }

// Response (token shown ONCE)
{
  "key": { "id": 12, "label": "production server", "prefix": "aB3xY7zQ", ... },
  "token": "bgl_aB3xY7zQmN9pK2rT5sW8vCdE"
}
DELETE/api/api-keys/{id}

Revoke

Existing usages start failing with 401 unauthorized on the next request.

Public short-link resolver

GEThttps://buygolinks.com/{code}

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): builds https://{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.
  • Non-Amazon link: 302 directly to destination_url.
GEThttps://buygolinks.com/c/{alias}

Collection rotator

No auth. Picks a random member of the collection identified by the rotator alias, then resolves it as above.