Skip to content
Black BoxPersonalization

Calls

Call a public API from the page you already have open.

The Sources lab pauses a practice file and steps through it. This page is the next move: change that kind of file so it asks this site for information, then read the answer. The two routes are GET /api/data-centers and GET /api/collect. Neither write is part of the exercise.

The shortcuts are for Chrome on Mac and on Windows. On a Mac laptop, the F keys often need Fn as well, unless the keyboard uses them as standard function keys. The Command shortcuts do not need Fn. Hover a toolbar button to confirm the shortcut in the Chrome you have open.

fetch from the Console

Click the page once so the keyboard shortcut reaches the browser, then open the Console.

MacCommand + Option + JWindowsControl + Shift + J

Sources is the other panel you will use. Open it with Command + Option + I on Mac, or F12 or Control + Shift + I on Windows, then click Sources. Escape shows the Console as a drawer under Sources, which is the useful layout once you are paused. Command + Shift + P on Mac, or Control + Shift + P on Windows, opens the Command Menu if a panel is hidden. Type Show Console or Show Sources.

The console runs in this page. A fetch from here is the same as a fetch the page made. Paste this and press Enter. Top-level await works in the Chrome console, so the line waits for the response:

const response = await fetch("/api/collect", { method: "GET", cache: "no-store" })
response.status

200 means the route answered. The body is a later step. Leave the method as GET.

The same call, paused and unpaused

Unpaused, the page’s event loop is running. The console sends the request and the promise settles on its own. You can read response.status on the next line.

Paused, Chrome has stopped the page’s JavaScript on a breakpoint. The console then evaluates in the selected call-stack frame, so you can see local variables such as path. A fetch you start from that prompt can leave the browser, but the page cannot run the continuation that reads the body until you resume. If an await sits there and never finishes, resume, then look again.

MacF8 or Command + \\WindowsF8 or Control + \\

That resumes until the next breakpoint. Command + F8 on Mac, or Control + F8 on Windows, deactivates breakpoints when you want the rest of the click to finish.

Use the unpaused console when you want the answer immediately. Use the paused console when you want to call from inside a function and inspect the locals around that call.

DevTools Snippets

A snippet is a script Chrome keeps in DevTools, not on this site. It runs in the page, so it can see bbpDebugCalls and it can fetch this origin. Open the Command Menu and run Create new snippet, or open Sources and choose Snippets in the left pane.

With the snippet focused, run it.

MacCommand + EnterWindowsControl + Enter

Command + P on Mac, or Control + P on Windows, then a name that starts with !, also runs a saved snippet. A snippet is the right place for a call you want to repeat. It is still just this page: it cannot read another origin that did not allow it, and it should not POST.

Local Overrides

A live edit lasts until reload. An override lasts longer, because Chrome serves a file from a folder you picked instead of the network. In Sources, open Overrides and choose Select folder for overrides. Chrome asks for permission to that folder. After that, a save in a file from this origin writes the override.

Three kinds of override matter here:

  • A JavaScript file. Open /debug/api-calls.js, change callApis to true, and save. Reload. The button then runs the copy from your folder. Delete the override, or clear it from the Overrides pane, when you want the server file back.
  • Response content. In Network, right-click a finished GET to this origin and choose Override content. Chrome opens the body. You can change a copy of the JSON to see how a reader handles a different payload. Remove it before you decide what the live route returns.
  • Response headers. Right-click that same GET and choose Override headers. You can add a header on a response this page was already allowed to read, then reload and look at response.headers. That is a local copy.

An override does not make a blocked cross-origin response readable, and it does not grant a cookie or a token you do not have. Do not invent an Access-Control-Allow-Origin header to reach someone else’s API.

Edit a function live in Sources

Open the practice file. Command + O or Command + P on Mac. Control + O or Control + P on Windows. Type api-calls. It sits under this host, in debug. Jump to the function with Command + Shift + O on Mac, or Control + Shift + O on Windows, and type readPracticeCalls.

The file is unminified on purpose, including in production, so the names and line breaks you see are the ones in public/debug/api-calls.js. Change the body of readPracticeCalls. Save.

MacCommand + SWindowsControl + S

Chrome patches that function in this tab. The next click uses the new body. A save that only changes a line outside the function does not rerun the script. Put the edit inside readPracticeCalls. Reload throws the patch away unless Local Overrides is keeping the file.

Copy as fetch and Copy as cURL

After a call has finished, open Network and click the request. Right-click it and choose Copy, then Copy as fetch. Paste that into the unpaused console. It is a fetch with the URL and headers Chrome recorded. Edit the query, for example change Dallas to another city on /api/data-centers, and run it again. Keep the method GET.

Copy as cURL (bash) on Mac, and Copy as cURL (cmd) or Copy as PowerShell on Windows, builds a shell command for the same request. Replay XHR, on the same right-click menu, sends the request again from the browser without a paste.

Chrome includes the Cookie header in that copy when the browser sent one. bbp.visitor is HttpOnly, so page JavaScript cannot read it, but a copied cURL line can still contain it. Delete Cookie and Authorization before you save or share the command. Replaying a copied cookie is replaying a credential.

Status, headers, and timing

The response object is not the JSON. Read the status before you parse the body:

response.status
response.ok
response.headers.get("content-type")
await response.json()

headers is a list, not a plain object. get reads one name. The data-center route adds X-Upstream-Cache and, when it called Wikidata, a Server-Timing header. The collect route’s JSON has store, driver, and records. The practice file reports the count of records and drops the rows.

In Network, open the request and then Timing. Queueing, request sent, waiting for server response, and content download are the parts of that one exchange. The practice file also times the fetch with performance.now, which is the wait inside the page, not the breakdown in the Timing tab. Resource Timing, performance.getEntriesByType("resource"), is the same family of numbers for every request this document made.

Why CORS, cookies, and tokens stop a script

A script can ask for any URL. The browser decides what the script is allowed to read back. Same-origin calls on this host are readable. A call to another origin is readable only if that server sends Access-Control-Allow-Origin for this page. If it does not, the console shows a CORS error and the promise rejects. That is the browser enforcing the other site’s header, not a puzzle to route around.

Cookies follow a second rule. A same-origin fetch sends this site’s cookies, including HttpOnly ones, without JavaScript reading them. A cross-origin fetch omits cookies unless you set credentials: "include" and the other server explicitly allows this origin and credentials. An Authorization header is not added for you. Putting a token in a header makes a preflight, and the other server still has to allow that header. A token in localStorage on this origin is not visible to another origin.

This site’s Content-Security-Policy also limits connect-src. The console on this page can call this origin. It is not a general client for the rest of the web. The data-center search reaches Wikidata from the server, which is why the practice script calls /api/data-centers instead of the query service.

Practice: edit the file so it calls

The file is /debug/api-calls.js. The function is readPracticeCalls. It already contains the two GET calls. callApis is false, so it returns before either of them. The button draws whatever the function returns.

/api/data-centers?country=US&city=Dallas&name=DataBank is the same search as the API lab. GET /api/collect is the status the Collection Lab polls: where the rows live, which driver is open, and how many rows this browser’s visitor id has. The practice file does not POST, so it does not store a beacon.

This tab only

Run the practice calls

The button calls readPracticeCalls and draws the return value. Until you edit the file, that value says the calls are off. After you save callApis = true, the same button runs two GET requests and shows their status.

No run yet.

  1. Wait until the button reads Run the practice calls. That means the practice file has loaded. Click it once. The panel should say callApis is false.
  2. Open /debug/api-calls.js from Sources. Command + P on Mac, Control + P on Windows. Confirm the line breaks match the file, not a one-line bundle.
  3. Jump to readPracticeCalls. Change const callApis = false to const callApis = true.
  4. Save with Command + S on Mac or Control + S on Windows. For a copy that survives reload, turn on Local Overrides first, then save, then reload.
  5. Click Run the practice calls again. The panel should show a status and a time for each GET. A data-center row count and a collect record count are enough. The first facility name is a label, not something you need to match.
  6. Optional. Set a line breakpoint on the fetch inside readGet, click the button, and try fetch(path) from the paused console. Resume with F8 so the button can draw the result.
  7. Optional. In Network, right-click the data-center GET, Copy as fetch, change the city, and run that paste from the unpaused console. Leave it as GET.

What you may call

Use this lesson on your own browser and on this site’s public GET routes, or on an API that is genuinely public and allows the call. These pages are a lesson. They are not permission to try an account, a session cookie, or a token that is not yours, and they are not permission to ignore another site’s terms, its CORS headers, or its rate limits.

One data-center search and one collect status read are enough. The search is cached here and the upstream is Wikidata, which asks callers not to hammer the query service. Do not loop it. Do not point the practice function at a POST, including POST /api/collect. Consent still decides whether a beacon is stored. This button does not send one.

The Sources lab is the pause-and-step half of this walkthrough. Its practice file does not call fetch. This one does, after you edit it.

Why these picks

Reading path transitions…