Skip to main content

Confirm

Are you sure?

Use cases

URL features for your customers

Your users paste links; your app reads, previews and imports them. The per-tenant accounting and the safe retries are built in.

By · Last updated: September 2026

TL;DR

To embed URL features in a multi-tenant SaaS, send every request with your tenant's id as a label, retry with an Idempotency-Key so a timeout never bills twice, and keep one project per environment. URLpipe returns the label with every result, totals credits per label each month, and processes pages in the EU on every plan.

Free plan, no credit card. 1,000 credits a month.

The job

What changes when the URLs belong to your customers

A SaaS that reads links for its users — a CRM that imports a company's website, a knowledge tool that ingests help-centre pages, a newsletter app that unfurls every link — has problems a single-tenant script does not. You need to know which tenant spent what. A retried job must not charge twice. A European customer's procurement team will ask where the pages are processed. And one tenant's burst must not starve the others.

The endpoints are the ordinary ones — /markdown to read a page, /meta for a preview card, /html when you parse the DOM yourself. What this page is about is the four things around them.

Design

Four things to wire in

A label per tenant

Send {"tenant": "t_8812", "feature": "import"} as labels. They come back in the webhook, so a result routes to its tenant without a lookup, and the project's Labels page totals credits per tenant per calendar month — the number you need to pass costs on, or to spot the tenant whose usage doesn't match their plan. Use ids, not names or emails: labels appear in your dashboard and webhooks.

Retries that never bill twice

Your job runner will retry. Send an Idempotency-Key header derived from your job id, and a resend within 24 hours returns the first request's token and result — one charge, one webhook. An identical request that arrives while the first is still running waits for it, for free, with or without a key. See retries & duplicates.

One project per environment

Each project has its own API key, webhook endpoint, signing secret, robots.txt setting and history. Keep production and staging apart, so a test run never shows up on a tenant's usage. Cached results are shared across your organization's projects, so staging reading a page production already fetched is a free hit.

EU processing

Pages are fetched, rendered and stored in Europe on every plan. /markdown and /html never call a model, so they never leave it. Switch the organization to EU processing and the three AI operations — /meta, /summarize, /keywords — run on EU-only providers too, at no extra charge.

Flow

A tenant's import, end to end

  1. 1
    A tenant pastes a URL. Your app enqueues a job with its own id.
  2. 2
    The worker posts the request async, with the tenant label, Idempotency-Key set to the job id, and your webhook as report_to.
  3. 3
    If the worker crashes and the job runs again, the same key returns the same token — nothing new runs.
  4. 4
    The signed webhook arrives with the labels; your handler writes the result to the tenant's record.
Enqueue a tenant's import
import os, requests

def import_page(job_id: str, tenant_id: str, url: str) -> str:
    res = requests.post("https://urlpipe.dev/scrape",
        headers={
            "Authorization": f"Bearer {os.environ['URLPIPE_API_KEY']}",
            "Idempotency-Key": f"import-{job_id}",
        },
        json={
            "url": url,
            "operations": ["markdown", "meta"],
            "report_to": "https://app.example.com/webhooks/urlpipe",
            "labels": {"tenant": tenant_id, "feature": "import"},
        }, timeout=30)

    if res.status_code == 429:           # rate_limited or concurrency_limit
        raise RetryLater(res.json()["error"])
    res.raise_for_status()
    return res.json()["token"]

Throughput is shared: 60 requests a minute and 15 per 10 seconds per project on every plan, and your plan's parallel limit — 8 on Pro, 20 on Scale — across all of them. Put a queue with per-tenant fairness in front, and treat 429 as "try again shortly": the error field says whether it was the rate, the parallel limit or the month's credits.

Credits

What it costs

Four hundred tenants pasting a hundred links each a month, read and carded, at list price:

WhatA monthCredits eachCredits
Links your users paste, read as Markdown40,000140,000
The same links' metadata, for the card40,0005200,000
Repeats inside max_age (cache hits)25,00000

240,000 credits; the cheapest way to buy it is Scale ($149, 175,000 credits) plus 65,000 credits of overage at $1.50 per 1,000 credits — $246.50 a month. The cache row is the one to watch: tenants paste the same popular links, and every repeat inside max_age is free while each tenant still gets its own token and labels. Paid plans are never cut off mid-month — past the allowance, credits are billed at the overage rate on a separate invoice.

Limits

What URLpipe does not do for a multi-tenant app

  • Sub-accounts or per-tenant keys. Tenants are labels on your requests, not accounts. Your users never see URLpipe.
  • Per-tenant quotas. Usage is reported per label; enforcing a tenant's limit is your code.
  • Invoice your tenants. The Labels page gives you the numbers; billing them stays in your app.
  • Fetch private URLs. Your tenants' intranet pages and anything behind a login are out of reach — only public http(s) addresses.

FAQ

Frequently asked questions

How do I see what each tenant used?
Label every request with the tenant id. The project's Labels page totals requests, failures and credits per label value for each calendar month, with what unlabelled requests spent, so the figures add up.
What happens if my worker retries a request?
With the same Idempotency-Key within 24 hours, the retry returns the first request's token and result: one charge, one webhook. Reusing a key for a different request is refused with a 422.
Can I keep all processing in the EU?
Fetching, rendering and storage are in Europe on every plan. Switching the organization to EU processing moves the AI operations to EU-only providers too, at no extra charge.
Do two tenants fetching the same URL pay twice?
No. Results are cached per organization, so the second request inside max_age is a free cache hit — and each request still keeps its own labels and token.
Do rate limits go up on bigger plans?
No. Every plan allows 60 requests a minute and 15 per 10 seconds per project. Bigger plans raise the monthly credits and the number of requests running in parallel.

Build it on the free plan.

Free plan, no card. Confirm your email and your API key is live — you'll be making real requests in minutes.