import type { Metadata } from "next"; import { PLAN_LIMITS } from "@fetcha/core"; import { CodeBlock } from "@/components/ui/code-block"; import { Badge } from "@/components/ui/badge"; import { DocPage } from "@/components/docs/doc-page"; import { A, Code, H2, H3, Li, P, Strong, Table, TBody, Td, Th, THead, Tr, Ul } from "@/components/docs/prose"; import { Callout } from "@/components/docs/callout"; import { CodeTabs } from "@/components/docs/code-tabs"; import { ResponseExample } from "@/components/docs/response-example"; import { fetchTabs } from "@/components/docs/snippets"; export const metadata: Metadata = { title: "Network Selection", description: "Network classes, what auto does, how routes are scored, per-domain intelligence, escalation, circuit breakers and which classes are available (all of them).", }; const CLASSES: Array<{ name: string; live: boolean; desc: string }> = [ { name: "auto", live: true, desc: "Let the engine choose. Cheapest class first, escalating to more reliable classes on blocks. Resolves to residential today." }, { name: "residential", live: true, desc: "Exit IPs assigned by consumer ISPs to households. Highest acceptance on sites that filter datacenter traffic. Geo-targetable." }, { name: "datacenter", live: false, desc: "Exit IPs from hosting providers. Fastest and cheapest, most often blocked. Included, no live route yet." }, { name: "isp", live: false, desc: "Static IPs registered to consumer ISPs but hosted in datacenters. Residential reputation with datacenter speed. Included, no live route yet." }, { name: "mobile", live: false, desc: "Exit IPs from cellular carriers. Highest acceptance, highest cost. Included, no live route yet." }, ]; const ALL_NETWORKS = ["datacenter", "residential", "isp", "mobile"] as const; const L = PLAN_LIMITS.unlimited; export default function NetworksPage() { return (

Network classes

The network field of a fetch request accepts one of five values. Classes describe the kind of exit IP; the upstream networks behind each class are managed by Fetcha and never exposed.

{CLASSES.map((c) => ( ))}
Value Status Description
{c.name} {c.live ? "Live" : "Coming soon"} {c.desc}
Only the residential class has live capacity. auto therefore resolves to residential and metadata.network reports {`"residential"`}. Requesting{" "} datacenter, isp or mobile explicitly returns 400 NETWORK_UNAVAILABLE because the class has no live route yet — never because of your plan: all classes are included for every organization. When those classes launch, auto will start using them without any change on your side.

What auto does

auto is the default and the recommended setting. For each request the routing engine builds an ordered list of candidate routes (a route is one upstream network serving one class), then executes them in order until one succeeds or the retry budget is spent.

1. Eligible classes

The engine starts from the escalation order datacenter → isp → residential → mobile (all four classes are available to every organization) and drops classes that have no configured, healthy route right now. If the domain has an admin-pinned force_network policy, only that class is considered.

2. Scoring

Every eligible route receives a score between 0 and 1, computed as a weighted sum of six signals:

Weight Signal How it is measured
35 % Historical success Success rate of this route on this domain, Bayesian-smoothed toward a per-class prior (datacenter 70 %, isp 85 %, residential 93 %, mobile 95 %) so new domains start with sensible expectations.
20 % Cost Price per GB of the route, normalised against the most expensive class (about $15/GB).
15 % Latency Average latency observed on this domain (300 ms scores 1.0, 5 s scores 0). Without history, datacenter assumes 500 ms and other classes 1,200 ms.
15 % Network health Health factor from the route's circuit breaker: 1.0 when closed and clean, degraded by recent failure ratio, 0.3 while half-open, 0 while open.
10 % Geography Whether the route can serve the requested country, region and city.
5 % Session stability Whether the route supports sticky sessions when the request carries one.

3. Ordering and escalation

4. Learning

After every attempt, the domain profile is updated with the outcome (success, block, latency, cost). Profiles are shared across all Fetcha customers, so a site that starts blocking a class is learned once and avoided for everyone. Only aggregate statistics are stored; never your request or response content.

Circuit breakers

Each route has an in-memory circuit breaker so that an upstream incident does not turn into a wave of failed requests:

When every candidate route is open or unconfigured, auto requests fail fast with 503 PROVIDER_UNAVAILABLE and explicit-class requests with{" "} 400 NETWORK_UNAVAILABLE. Neither counts as a target failure and both are safe to retry after a short pause.

Availability

Fetcha has a single plan and no network class is gated: every organization may request any class. The only reason an explicit class is refused is the absence of a live route for it. The table reflects the platform configuration; remember that only residential is live today.

{ALL_NETWORKS.map((n) => { const live = CLASSES.find((c) => c.name === n)?.live ?? false; return ( ); })}
Class Included Live route
{n} {L.networks.includes(n) ? Yes : —} {live ? Yes : Not yet}

Choosing a class

Prefer auto. Pin residential when you need to guarantee that no cheaper class is ever tried on a sensitive target. Read the served class from metadata.network and, with debug: true, the per-attempt route aliases from metadata.debug.attempts.

); }