Use cases

How do I check my website's speed from different countries?

Run verify. UpRock's verify tool loads your URL on devices in North America, Europe and Asia-Pacific at the same time and returns, for each check, the country it ran in, TTFB, FCP, LCP, CLS, load time, transfer size and protocol, plus a report link you can share. Run it after each deploy and flag the regions over web.dev's thresholds, such as 0.8 s for TTFB and 2.5 s for LCP.

Updated · Read as markdown

What a verify run measures

A verify run loads one URL from several regions at once. Each check is a separate page load on a device in one of the regions you ask for, and all checks run at the same time, so a verify run never takes much longer than its timeout. Each completed check returns:

  • the country it ran in,
  • TTFB, FCP, LCP and CLS,
  • the DOMContentLoaded and load times,
  • the transfer size and HTTP protocol,
  • a screenshot, as a sweep:// resource you can read with resource_fetch.

The run as a whole returns a report_url, a page on uprock.ai you can send to someone who doesn't use the API.

The same tool is available over MCP (verify, formerly sweep) and in the CLI (uprock ai verify; the old name uprock ai sweep still works). The parameters, from the MCP server's own schema and uprock ai sweep --help on 25 Oct 2026:

MCP argumentCLI flagWhat it doesDefault
urlfirst argumentThe page to loadrequired
regions-r NA,EUAny of NA, EU, APAC, LATAM, MEANA, EU, APAC over MCP
tries--triesChecks per region5
device-dmobile or desktopmobile
timeout-tSeconds for the whole run60

The tool's own description says NA, EU and APAC "have the fastest and most reliable device pools", which matched what we saw. Verify takes regions, not countries, and the country changes from one verify to the next: our APAC checks came from Malaysia, Vietnam and Japan, and our EU checks from six countries. If you need one particular country, load the page with crawl_fetch and a country code instead.

What each number means

A verify run reports lab numbers from one page load, and web.dev's thresholds are written for the 75th percentile of real users' loads. They're still the right yardsticks for a smoke test (web.dev, checked 25 Oct 2026):

MetricWhat it measuresGoodPoor
TTFBFrom the start of navigation to the first byte of the response, including redirects, DNS, connection and TLS. Not a Core Web Vital, but it shows server and network delay0.8 s or lessover 1.8 s
FCPWhen the first text or image appears1.8 s or lessover 3.0 s
LCPWhen the largest image, text block or video in the viewport renders. A Core Web Vital2.5 s or lessover 4.0 s
CLSThe largest burst of unexpected layout shifts. A Core Web Vital0.1 or lessover 0.25
Load timeUntil the page's load event firesno threshold
Transfer sizeBytes of the HTML document over the wire, compressed, not the whole pageno threshold
ProtocolHTTP version the page was served over, such as h2

Transfer size is easy to misread. For the MDN page below, the run reported 21,172 bytes; the same HTML is 210,078 bytes uncompressed. INP, the third Core Web Vital, needs a real click or tap, so a verify run can't measure it.

Four sites from five regions

We swept four public pages of very different weight on 25 Oct 2026, between 15:19 and 15:31 UTC, one check per region, on the default mobile device. Times are in milliseconds; bold means worse than web.dev's "good" threshold.

PageRegion (country)TTFBFCPLCPLoadHTML transfer
example.comNA (US)587876not measured524634 B
EU (IT)2031,4921,492335634 B
APAC (MY)506796not measured784634 B
MDN, 403 ForbiddenNA (US)3161,2961,2961,05321 KB
EU (GB)6441,9321,9321,88321 KB
APAC (MY)8172,0002,0001,87021 KB
Wikipedia, Web performanceNAno result
EU (HU)1,1643,0603,2925,38827 KB
APAC (MY)7391,0841,0841,28827 KB
The Verge, home pageNA (CA)5321,7528,23223,594926 KB
EU (DE)6271,6562,9529,08264 KB
APAC (MY)9381,8083,1968,46064 KB

LATAM and MEA returned no result for any of the four pages. CLS was 0 in every check, and every protocol was h2 except The Verge from Canada, which reported none.

What the table shows:

  • The light pages stayed close to "good" everywhere they ran. example.com's TTFB stayed between 203 and 587 ms. MDN's LCP was under 2.5 s in every region, though its FCP was just over 1.8 s from the UK and Malaysia, and its TTFB 17 ms over 0.8 s from Malaysia.
  • The heavy page missed LCP in every region that answered. The Verge's largest element took 2.9 to 8.2 seconds, and its load event came 8 to 24 seconds in.
  • One check is one device. Wikipedia was fast from Malaysia and slow from Hungary. With one check per region you can't tell a slow region from a slow phone, which is why the code below flags a region only when every check there is slow, and why tries: 2 is worth it for a release check.
  • The Canada check is an outlier. It reported no protocol and a transfer size equal to the uncompressed HTML, as if the page came without compression. Treat a single odd check as a reason to re-run, not as a finding.

What we learned running it

In the CLI, one silent region still loses the whole run. Asked for all five regions, the tool answered "sweep deadline exceeded" with no results, even though the EU and APAC checks finished in seconds when we ran them alone. MCP was fixed on 30 Oct: it now returns the regions that finished, with a no_device or incomplete reason for the rest. The CLI hasn't changed yet, so the code still checks each region in a separate call, in parallel, and keeps whatever comes back.

Keep the timeout at 60. The run's deadline was about 30 seconds past its timeout: 90 seconds for 60, 120 for 90. With a timeout of 120, the CLI's connection was reset after 125 seconds instead.

A zero means "not measured", not "fast". example.com from Italy once came back with a TTFB of 369 ms and every other metric at 0, and two of its three checks in the table had an FCP but an LCP of 0. A URL that always answers 500 also completed, with only a TTFB. The code skips a threshold when its metric is 0.

Compare a metric across regions, not metrics within a check. The timings come from different timers: example.com from the US reported a TTFB larger than its load time.

Check every region after a deploy

Both versions check each region in parallel with one check, print each region's country, TTFB and LCP, flag the ones over 0.8 s TTFB or 2.5 s LCP, and exit with code 1 if any region is over, so CI can stop a rollout. A region with no result is printed but doesn't fail the run. Pass the regions as the second argument; the default is the three the tool calls most reliable.

// npm i @modelcontextprotocol/sdk   (Node 18+)   env: UPROCK_API_KEY// After a deploy: node check-regions.ts https://your-site.example NA,EU,APAC// Exits 1 if any region is over the thresholds, so CI can stop the rollout.import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";​const MAX_TTFB_MS = 800; // web.dev: a good TTFB is 0.8 s or lessconst MAX_LCP_MS = 2500; // web.dev: a good LCP is 2.5 s or lessconst TRIES = 1; // checks per region; 2 smooths out one slow device​type Check = {  status: string;  country?: string;  error_message?: string;  metrics?: { ttfb: number; fcp: number; lcp: number; total_load_time: number };};type RegionResult = {  region: string;  checks: Check[];  report?: string;  error?: string;};​const uprock = new Client({ name: "region-check", version: "1.0.0" });await uprock.connect(  new StreamableHTTPClientTransport(    new URL("https://mcp.uprock.ai/mcp"),    { requestInit: { headers: { Authorization: `Bearer ${process.env.UPROCK_API_KEY}` } } },  ),);​const textOf = (content: unknown) =>  (content as { type: string; text?: string }[]).find((c) => c.type === "text")?.text ??  "";​// One run per region, run in parallel, so each region reports on its own and a slow// region can't hold up the rest.async function sweepRegion(url: string, region: string): Promise<RegionResult> {  try {    const res = await uprock.callTool(      {        name: "verify",        arguments: {          url,          regions: [region],          tries: TRIES,          device: "mobile",          timeout: 60, // the run gives up about 30 s after this        },      },      undefined,      { timeout: 120_000 },    );    if (res.isError) return { region, checks: [], error: textOf(res.content) };    const result = JSON.parse(textOf(res.content));    return {      region,      checks: result.results?.[region]?.jobs ?? [],      report: result.report_url,    };  } catch (err) {    return { region, checks: [], error: (err as Error).message };  }}​// What's wrong with one completed check, if anything. A check can complete with only a// TTFB (every other metric 0), and LCP is sometimes 0 when FCP isn't: treat 0 as "not// measured", never as fast.function problems({ metrics: m }: Check): string[] {  const out: string[] = [];  if (m && m.ttfb > MAX_TTFB_MS) out.push(`TTFB ${m.ttfb} ms > ${MAX_TTFB_MS}`);  if (m && m.lcp > MAX_LCP_MS) out.push(`LCP ${m.lcp} ms > ${MAX_LCP_MS}`);  return out;}​const [url = "https://example.com", regions = "NA,EU,APAC"] = process.argv.slice(2);let slow = 0;try {  const results = await Promise.all(    regions.split(",").map((region) => sweepRegion(url, region.trim())),  );  for (const { region, checks, report, error } of results) {    const done = checks.filter((c) => c.status === "completed");    if (!done.length) {      const why = error ?? checks.map((c) => c.error_message).join("; ");      console.log(`${region.padEnd(5)} no result: ${why}`);      continue;    }    // Flag a region only when every completed check there is over a threshold.    const flagged = done.every((c) => problems(c).length > 0);    if (flagged) slow++;    for (const c of done) {      const { ttfb = 0, lcp = 0 } = c.metrics ?? {};      const issues = problems(c);      console.log(        `${region.padEnd(5)} ${c.country ?? "--"}  TTFB ${ttfb} ms  ` +          `LCP ${lcp ? `${lcp} ms` : "not measured"}  ` +          (issues.length ? `SLOW: ${issues.join(", ")}` : "ok"),      );    }    console.log(`      report: ${report}`);  }} finally {  await uprock.close();}process.exit(slow ? 1 : 0);

A run against the MDN page from all five regions printed this (report links shortened). LCP wasn't measured in two of the checks, so only TTFB was compared there, and the run took 90 seconds because LATAM and MEA waited for the deadline:

NA    US  TTFB 473 ms  LCP not measured  ok      report: https://uprock.ai/verify/report/5zj31doX…EU    NL  TTFB 716 ms  LCP not measured  ok      report: https://uprock.ai/verify/report/dYftiv7X…APAC  VN  TTFB 484 ms  LCP 1796 ms  ok      report: https://uprock.ai/verify/report/iuiwAxKn…LATAM no result: sweep deadline exceededMEA   no result: sweep deadline exceeded

Tested 25 Oct 2026 against the live UpRock API (MCP SDK 1.32.2, uprock CLI 0.0.37). The TypeScript version ran on the MDN page from all five regions (output above, exit code 0) and on The Verge from APAC (LCP 12,164 ms from Japan, exit code 1). The Python version ran on The Verge with the default regions: EU (Norway) and APAC (Malaysia) were flagged for TTFB and LCP, NA returned no result, and it exited with code 1. We didn't run the code with tries above 1 or with the desktop device. On 6 Nov 2026 we ran the TypeScript version again with an UpRock API key in an Authorization header, from NA and EU.

FAQ

Can I choose the exact country a check runs in?

No. Verify takes regions, and each check runs in whichever country's device picks it up. In our runs on 25 Oct 2026, APAC checks came from Malaysia, Vietnam and Japan, and EU checks from Italy, the Netherlands, the UK, Hungary, Germany and Norway. To load a page in one specific country, use crawl_fetch with a country code instead.

Why did a region come back with no result?

No device took the check before the deadline. The tool returns "sweep deadline exceeded" and, in the CLI, a run with several regions returns no results at all. Over MCP, since 30 Oct 2026, the regions that finished come back, and each of the others says why it has no result. On 25 Oct 2026 this happened to every LATAM and MEA sweep we ran and to some NA sweeps. We tried again on 29 Oct at 04:20 UTC, after a fix on UpRock's side: LATAM, MEA and NA (night there) got no device in any of five tries each, on mobile or desktop, while EU and APAC answered every time within 10 seconds. It says nothing about your site, so don't fail a deploy on it.

Does a verify run report the HTTP status code?

No. A check against a URL that always answers 500 came back as completed, with a TTFB and every other metric at 0. Check the status from your CI with a plain request, and use the run for timings.

Are these my Core Web Vitals?

Not quite. web.dev assesses Core Web Vitals at the 75th percentile of real users' page loads, and INP needs a real interaction. A verify run is one page load per check on one device, so use it as a smoke test right after a deploy, not as a replacement for field data.

What does a verify run cost?

A verify run does one check per region per try, so the code on this page keeps tries at 1. Each check loads the page on a real device and returns a screenshot and Core Web Vitals, at $1.50 per 1,000 checks by card or USDC ($1.20 with UPT). A verify run of 3 regions with 5 tries each is 15 checks. For comparison, Checkly lists browser checks at $5 per 1,000 runs.

Other use cases to explore

References

Get started

Add UpRock in one paste.

Paste this into Claude Code, Codex, Cursor or any agent with a terminal. It reads our docs, sets up the MCP server or CLI, and tells you the cost before it spends anything.

Set up UpRock for me: read https://uprock.ai/agents.md, connect the UpRock MCP server, and run one test crawl. Tell me what it costs before you spend anything.

How do I check my website's speed from different countries? | UpRock AI