filoo

Filoo API

Programmable links for AI agents.

Create, observe, and modify short links via REST. Authenticate with a Bearer token. Ship faster than building it yourself.

REST · JSON · Bearer authTypeScript & Python SDKs100-1000 req/min depending on tier

Coming soon: MCP server for native integration with Claude, ChatGPT, and other MCP-aware agents. Get notified →

60-second quick start

# 1. Sign up at filoo.app, claim a username, subscribe to Pro or Agents
export FILOO_KEY="flo_live_..."

# 2. Create your first link
curl -X POST https://filoo.app/api/v1/links \
  -H "Authorization: Bearer $FILOO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": "https://example.com/long-url",
    "slug": "campaign-2025"
  }'

# Response:
# {
#   "code": "yourname:campaign-2025",
#   "short_url": "https://filoo.app/yourname/campaign-2025",
#   "destination": "https://example.com/long-url",
#   "created_at": 1736789012,
#   "active": true
# }

# 3. Read the stats anytime
curl https://filoo.app/api/v1/links/yourname:campaign-2025/stats \
  -H "Authorization: Bearer $FILOO_KEY"

That's it. Your link is live. Every scan is tracked.

Authentication

All API requests require a Bearer token in the Authorization header. Tokens are prefixed `flo_live_` and shown only once at creation time.

Generate keys at filoo.app/dashboard/api-keys.

Authorization: Bearer flo_live_xxxxxxxxxxxxxxxx

Each Pro account can issue up to 5 keys. Agents up to 50. Revoke any key at any time. The previous one stops working immediately.

Keys are HMAC-hashed in our database. We literally cannot recover yours if you lose it. Generate a new one and rotate.

Endpoints

GET/api/v1/me

Get current user. Returns information about the authenticated user, including tier, limits, and usernames owned.

curl https://filoo.app/api/v1/me \
  -H "Authorization: Bearer $FILOO_KEY"
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

const me = await filoo.me();
console.log(me.tier, me.limits.active_links);
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

me = filoo.me()
print(me['tier'], me['limits']['active_links'])
Response schema
{
  "id": "01HK...",
  "email": "you@yours.com",
  "primary_username": "agency",
  "usernames": ["agency", "client1"],
  "tier": "pro",
  "boost_credits": 500,
  "limits": {
    "active_links": 5500,
    "links_per_day": 200,
    "api_rate_per_min": 100,
    "usernames": 5
  },
  "is_admin": false
}
GET/api/v1/usage

Get current usage vs quotas. Returns today's links_created and api_calls along with current active_links count and the cap.

curl https://filoo.app/api/v1/usage \
  -H "Authorization: Bearer $FILOO_KEY"
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

const u = await filoo.usage();
console.log(`${u.active_links}/${u.active_links_limit} active links`);
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

u = filoo.usage()
print(f"{u['active_links']}/{u['active_links_limit']} active links")
Response schema
{
  "date": "2026-05-10",
  "links_created_today": 14,
  "links_created_limit": 200,
  "api_calls_today": 387,
  "active_links": 412,
  "active_links_limit": 5500,
  "boost_credits": 500
}
GET/api/v1/usernames

List your usernames. Returns the array of usernames the authenticated user owns.

curl https://filoo.app/api/v1/usernames \
  -H "Authorization: Bearer $FILOO_KEY"
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

const { usernames } = await filoo.usernames();
for (const u of usernames) console.log(u.username, u.is_primary);
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

result = filoo.usernames()
for u in result['usernames']:
    print(u['username'], u['is_primary'])
Response schema
{ "usernames": [ {"username": "agency", "is_primary": 1, "created_at": 1736789012} ] }
POST/api/v1/linksscope: links:write

Create a link. Create a permanent short link under one of your usernames. Counts against your daily creation quota and active link cap.

curl -X POST https://filoo.app/api/v1/links \
  -H "Authorization: Bearer $FILOO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"destination":"https://example.com","slug":"q4-launch"}'
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

const link = await filoo.links.create({
  destination: 'https://example.com',
  slug: 'q4-launch',
});
console.log(link.short_url);
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

link = filoo.links.create(
    destination='https://example.com',
    slug='q4-launch',
)
print(link['short_url'])
Request body schema
{
  "destination": "https://example.com/long-url",
  "username": "agency",
  "slug": "campaign-1",
  "label": "Spring 2026"
}
Response schema
{
  "code": "agency:campaign-1",
  "short_url": "https://filoo.app/agency/campaign-1",
  "destination": "https://example.com/long-url",
  "slug": "campaign-1",
  "username": "agency",
  "created_at": 1736789012
}
GET/api/v1/linksscope: links:read

List your links. Cursor-paginated list of your links, newest first. `limit` max 100.

curl "https://filoo.app/api/v1/links?limit=20" \
  -H "Authorization: Bearer $FILOO_KEY"
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

const { items, cursor } = await filoo.links.list({ limit: 20 });
for (const l of items) console.log(l.code, l.destination);
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

result = filoo.links.list(limit=20)
for l in result['items']:
    print(l['code'], l['destination'])
Response schema
{ "items": [...], "cursor": "1736789012" }
GET/api/v1/links/{code}scope: links:read

Get a link. Returns full link details for the given `code` (URL-encode the colon as `%3A`).

curl https://filoo.app/api/v1/links/agency%3Acampaign-1 \
  -H "Authorization: Bearer $FILOO_KEY"
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

const link = await filoo.links.get('agency:campaign-1');
console.log(link.destination);
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

link = filoo.links.get('agency:campaign-1')
print(link['destination'])
Response schema
{ "code": "agency:campaign-1", "destination": "...", ... }
PATCH/api/v1/links/{code}scope: links:write

Update a link. Update destination, label, show_on_profile, or active. Past destinations are kept in the link_destinations_history table.

curl -X PATCH https://filoo.app/api/v1/links/agency%3Acampaign-1 \
  -H "Authorization: Bearer $FILOO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"destination":"https://new-target.com"}'
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

await filoo.links.update('agency:campaign-1', {
  destination: 'https://new-target.com',
});
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

filoo.links.update('agency:campaign-1', destination='https://new-target.com')
Request body schema
{ "destination": "https://new-target.com", "label": "Updated" }
Response schema
{ "ok": true }
DELETE/api/v1/links/{code}scope: links:write

Delete a link. Permanent deletion. Scan history is preserved (no FK on scans.link_code).

curl -X DELETE https://filoo.app/api/v1/links/agency%3Acampaign-1 \
  -H "Authorization: Bearer $FILOO_KEY"
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

await filoo.links.delete('agency:campaign-1');
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

filoo.links.delete('agency:campaign-1')
Response schema
{ "ok": true }
GET/api/v1/links/{code}/statsscope: stats:read

Get link scan stats. Returns totals + top countries + top sources for the given link.

curl https://filoo.app/api/v1/links/agency%3Acampaign-1/stats \
  -H "Authorization: Bearer $FILOO_KEY"
import { Filoo } from '@filooapp/sdk';
const filoo = new Filoo({ apiKey: process.env.FILOO_KEY! });

const stats = await filoo.links.stats('agency:campaign-1');
console.log(stats.totals.total, stats.top_countries);
from filoo import Filoo
filoo = Filoo(api_key=os.environ['FILOO_KEY'])

stats = filoo.links.stats('agency:campaign-1')
print(stats['totals']['total'], stats['top_countries'])
Response schema
{
  "totals": { "total": 1247, "uniques": 893, "bots": 41 },
  "top_countries": [ {"name": "FR", "n": 612}, {"name": "BE", "n": 187} ],
  "top_sources": [ {"source": "qr-vitrine", "n": 412}, {"source": "qr-menu", "n": 289} ]
}

Outbound webhooks

Filoo sends a signed POST to your endpoint when events you subscribe to happen. Configure at filoo.app/dashboard/webhooks.

Subscribed event types:

link.createdlink.updatedlink.deletedlink.scannedlink.malicious_flagsubscription.activatedsubscription.canceledboost.purchasedaccount.username_claimed

Payload example:

{
  "id": "evt_01H8K...",
  "type": "link.scanned",
  "created_at": 1736789012,
  "data": {
    "link_code": "jr:q4-launch",
    "country": "FR",
    "device_type": "mobile",
    "referrer_host": "twitter.com"
  }
}

Each request includes an `X-Filoo-Signature` header containing `sha256=<hex>`. The signature is HMAC-SHA256 of the raw request body, using the webhook secret you configured.

# Use the TypeScript or Python tab — language-specific signature verification.
import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(body: string, signature: string, secret: string): boolean {
  const expected = 'sha256=' + createHmac('sha256', secret).update(body).digest('hex');
  return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
import hmac, hashlib

def verify(body: bytes, signature: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Replays: each event has a unique `id`. Your handler should be idempotent. We retry up to 5 times with exponential backoff if your endpoint returns non-2xx.

Rate limits

Per API key, sliding window: Pro: 100 requests / minute Agents: 1,000 requests / minute When exceeded, you get HTTP 429 with a `Retry-After` header in seconds.

HTTP/2 429
Retry-After: 18
Content-Type: application/json

{"error":"rate_limited","retry_after":18}

Daily creation limits are separate (200/day Pro, 5,000/day Agents). Exceeding those returns 429 with `error: "daily_limit"`. Active link limits are checked at creation, returning 429 with `error: "active_limit"` when full. Buy a Boost Pack or upgrade.

AI agent integration

Filoo is built for AI agents. Give your agent a single API key and let it create, track, and modify short links programmatically — no dashboard required.

How it works

Your agent gets one or more short links it can reuse across sessions. Each link is observable: the agent can read click totals, top countries, and referrer data at any time via the stats endpoint. And each link is editable: the agent can rotate the destination URL without changing the short link — perfect for A/B testing, campaign updates, or correcting a mistake after the link is live.

Example workflow

  1. 1. Agent calls POST /api/v1/links with a destination URL and a descriptive slug → receives { code, short_url }
  2. 2. Agent embeds the short_url in an email, Slack message, or generated document
  3. 3. When someone clicks the link, Filoo logs the scan with country, device, referrer, and timestamp
  4. 4. Agent calls GET /api/v1/links/{code}/stats → receives totals, top_countries, top_sources
  5. 5. Based on stats, agent calls PATCH /api/v1/links/{code} with a new destination URL → destination rotates, link unchanged
  6. 6. Loop: the agent reads stats, decides, and adapts — all via the same API.

Prompts for AI coding tools

Use these prompts with Cursor, Claude Code, Copilot, or any AI coding tool to integrate Filoo instantly:

Shorten a URL

"Create a function that shortens URLs using the Filoo API. Use POST /api/v1/links with Bearer token auth. Return the short_url from the response."

Read click stats

"Write a function that fetches click analytics for a Filoo short link using GET /api/v1/links/{code}/stats. Print total clicks and top countries."

Rotate destination

"Create a function that updates the destination URL of an existing Filoo short link using PATCH /api/v1/links/{code}. Accept the new URL as a parameter."

A/B test workflow

"Write a script that creates a Filoo short link pointing to variant A, waits for N clicks, reads the stats, then rotates the destination to variant B. Use the official @filooapp/sdk."

Best practices for agent-managed links

  • Use labels — set the label field on each link so your agent can identify links by purpose when listing them later.
  • Rotate destinations for A/B testing — keep the short URL constant, change the destination via PATCH, and compare stats before vs. after.
  • Monitor click analytics — have your agent poll stats periodically or subscribe to link.scanned webhooks for real-time feedback.
  • Set descriptive slugs — use human-readable slugs like campaign-q2-v1 so debugging via the dashboard stays easy.
  • Clean up inactive links — use DELETE /api/v1/links/{code} to retire links that have stopped receiving traffic, staying under your active link cap.
  • Use the Agents tier — 50,000 active links, 5,000 creations/day, 1,000 API req/min. Designed specifically for autonomous workloads.

Most useful endpoints for agents

The existing Filoo API already covers everything an agent needs. The most relevant endpoints are:

  • POST /api/v1/links — Create a new short link (agents can generate hundreds automatically)
  • GET /api/v1/links/{code}/stats — Read scan totals, countries, and referrers
  • PATCH /api/v1/links/{code} — Update the destination URL (rotate without breaking the short link)
  • GET /api/v1/links — List all links with cursor pagination (find links by label or date)
  • GET /api/v1/usage — Check daily creation quotas and active link counts before generating more

Error format

All error responses follow this shape:

{
  "error": "machine_readable_code",
  "message": "Human-readable explanation",
  "details": {}
}
Codes by HTTP status:

  400 invalid_input | 401 not_authenticated | 403 forbidden
  404 not_found     | 409 conflict          | 422 unprocessable
  429 rate_limited  | 500 server_error      | 503 service_unavailable

Specific 422 codes: malicious_destination, blocked_destination,
slug_reserved, username_inactive.
Specific 429 codes: rate_limited, active_limit, daily_limit.

Specification

The full schema is published as OpenAPI 3.0 at https://filoo.app/api/openapi.json Import into Postman, Insomnia, Swagger UI, or generate clients in any language with openapi-generator.

Changelog

Stable since v0.3.1 (2026-05-10). No breaking changes planned. Track at filoo.app/changelog.

Get an API key.

Pro plan starts at €9/month. Agents at €29/month. 14-day refund.