Lesson 4 Trick: Speak ~20 minutes

Reporting

Turn a nested API response into the exact spreadsheet somebody asked you for

Loading data is half the job. The other half is the sentence you hear on every project: “can you just send me that in a spreadsheet?”

Today’s version: the Golden Retriever Distribution Center wants everything that’s running low on stock, in Excel.

Everything you need is already in The Training Ground — you put it there in Lesson 2.

Step 1 — Pagination

An API will not hand you everything at once. Ask for 67,000 records and you get the first few hundred and a note saying there are more. That is every API you will ever meet.

Dobermann has a trick up its sleeve. It will walk the pages for you — and as each page comes back, its rows are added to the Completed tab. One click, one table, all of it.

Let’s see it in action. Copy this, open API Catalogue, and click New Endpoint from Clipboard:

// Name: Puppy School — Inventory Report
// Method: GET
// Path: /inventory
// QueryParam: page: {{A8:PAGE:0:totalCount}} [enabled]
// QueryParam: size: {{A8:SIZE:500:pageSize}} [enabled]

How Dobermann knows where to stop

Those two QueryParam lines are pagination variables, and they are the whole trick. {{A8:PAGE:0:totalCount}} starts at page 0 and reads totalCount out of each response to work out how many pages there are. {{A8:SIZE:500:pageSize}} asks for 500 a page and reads pageSize back to check the API agreed.

You will rarely type them. On an endpoint that has none, the Pagination button’s Settings tab reads a real response and writes them for you.

Save, and hit Run API. The Console opens with page one — 500 rows of 67,000-odd. Dobermann never walks a whole API without being asked.

So ask. In the Console footer click Pagination, choose Fetch all pages, and click Run.

Pages stream in several at a time and the row count climbs as they land. Dobermann loves to fetch.

Step 2 — Find the Data in the Response

Open the Raw tab and look at any one response. It’s an envelope again, with your records inside it:

{
  "totalCount": 67991,
  "pageSize": 500,
  "page": 0,
  "totalPages": 136,
  "data": [
    {
      "sku": "SKU-WOOF-001-00001",
      "gtin": "00012345600012",
      "description": "Premium Belly Rub Machine",
      "quantityOnHand": 12,
      "uom": "EA",
      "status": "low_stock",
      "location": { "gln": "0614141000012", "name": "Golden Retriever Distribution Center", "city": "Atlanta" },
      "product": {
        "sku": "SKU-WOOF-001", "gtin": "00012345600012", "category": "Grooming & Wellness", "unitPrice": 149.99,
        "reorderQty": 200, "supplierSku": "CTM-0001",
        "supplier": { "gln": "4012345000016", "name": "Chew Toy Manufacturing Inc" }
      }
    }
  ]
}

Your counts will differ from the example — they depend on how much you loaded in Lessons 2 and 3.

Two things worth noticing. The records are in data, as before, and back on the Completed tab Dobermann has already made each one a row — across every page, as one table. And each record carries nested objects — location, product, and a supplier inside the product — because the API resolves the reference data for you rather than leaving you with bare codes.

Step 3 — Build the View

The table shows everything, which is not the same as showing what was asked for. Nobody wants forty columns of internal identifiers.

Click the view button in the toolbar, then + Create View…

The View Editor opens with three regions:

  • Available Columns on the left — the full response tree, searchable
  • Selected Columns on the right — what ends up in your table
  • The row basis dropdown in the Selected panel header — which array produces one row each

Check the row basis is data, then tick these columns:

Column Where it comes from
sku the record — its own stock code, not product.sku, the catalogue’s code for the product
gtin the record — the product’s unique identifier
description the record
quantityOnHand the record
uom the record
status the record — you’re about to filter on it
location.name the nested location object
location.city the nested location object
product.category the nested product object
product.reorderQty the nested product object
product.supplierSku the nested product object — blank for some suppliers, and that’s fine
product.supplier.name two levels down: the supplier, inside the product
product.supplier.gln two levels down
location.gln the nested location object

gtin and the last five are for Lesson 5, which turns this report into purchase orders.

Notice what you just did. location.name lives one level down in the JSON and product.supplier.name two, and you’ve pulled them up alongside the record’s own fields as if they were flat columns. That is the whole point of a view — the API’s shape and the reader’s shape are almost never the same, and this is where you reconcile them without touching either.

Views work on errors too

Any tab takes a view, the Error tab included — the same three regions. That is how you turn a pile of failures into a table you can hand to whoever produced the file.

Name the view Low Stock by Location and save it.

Views save on the endpoint, so this one travels with it — export or share the endpoint and whoever receives it gets your columns and your row basis, not just the request.

Step 4 — Filter It in the Console

You have every inventory record on screen. The warehouse asked about one site’s low stock. You don’t go back to the API for that — you ask the table.

Click into the search box above the table and type:

low_stock

The row count drops to around eleven thousand: every record, at every site, whose status is low. Search looks across every column in the view, which is why status is one of them.

Now narrow it to the site that asked. A space and a + means and:

low_stock +Golden

About nine hundred rows — low stock, at the Golden Retriever Distribution Center. That is the report.

One more, because someone always asks. They don’t reorder by the case, so leave the CS rows out. A space and a - means not:

low_stock +Golden -"CS"

The quotes matter. Search normally matches part of a cell, and the letters “cs” turn up inside plenty of words — Electronics, Logistics. Quotes mean the whole cell, exactly, so only a unit of measure of CS is excluded. Try it without them and watch the table empty.

Four moves cover most of what you’ll ever need, and they light up in colour as you type so you can see Dobermann has understood them:

You type It means
low_stock +Golden both
low_stock -Golden the first, without the second
Golden,Labrador either
"CS" the whole cell is exactly this

There’s more — wildcards, how the pieces combine — in Console → Search.

Take the -"CS" back off, then click the quantityOnHand header to sort ascending. The most urgent records rise to the top.

Whatever you’ve got on screen is what gets exported: the same columns, the same order, the same sort, the same search. There’s no reshaping afterwards.

Step 5 — Send It to Excel

Open the Export menu and choose Excel.

That’s the spreadsheet the warehouse asked for: the right rows, the right columns, sensible headers, straight out of a live API — and no manual step between the API and the file. Only the rows your search left on screen are in it.

That’s the file you’d send. For the course, there’s a quicker way to show us.

Step 6 — Copy It, and Show Us

Not everything needs a file. Next to Export is Copy, and it puts the same table — same columns, same sort, same search — straight on your clipboard:

Copy → Pastes best into
Standard Outlook, Teams, Word, Confluence — arrives as a formatted table
Excel Excel, Google Sheets — keeps leading zeros on codes like GLNs
CSV scripts and anything that wants plain text
Markdown GitHub, Jira, Notion

Make sure your search still reads low_stock +Golden, open the Copy menu, choose Excel, and paste it here:

We check it against what the API actually holds for you, and tell you straight away if a row doesn’t belong or one is missing. Pass, and Lesson 5 unlocks.

(Copy → Standard and Copy → Excel stop at 1,000 rows, because pasting more than that tends to take the other application down with it. If yours is greyed out, the inventory file probably went in twice — reset with DELETE /my-data, reload it once, and fetch again.)

Bonus Credit — Ask the API a Narrower Question

You just pulled 67,000 records to keep 900. It worked, and it will work on any API — which is why it came first. But when an API can filter, let it. Less data over the wire, fewer pages, less load on a system you’re a guest on.

The Training Ground can. Open Puppy School — Inventory Report, click Duplicate, and on the copy use Add → Query Parameter twice:

// Name: Puppy School — Low Stock Report
// Method: GET
// Path: /inventory
// QueryParam: status: low_stock [enabled]
// QueryParam: locationGln: 0614141000012 [enabled]
// QueryParam: page: {{A8:PAGE:0:totalCount}} [enabled]
// QueryParam: size: {{A8:SIZE:500:pageSize}} [enabled]

Save, Run API, Pagination → Fetch all pages. Two pages instead of a hundred and thirty-odd, and the same rows you filtered your way to in Step 4. The duplicate kept your view, so the table already looks right.

status and locationGln are this API’s parameter names. The next API will call them something else, accept different values, or not offer them at all — its documentation is where you find out. Ours is here. The habit to keep is the order you ask in: filter at the API where you can, and in the Console where you can’t.

🐾 Dobermann Philosophy

An API response and a useful report are different shapes, and every tool makes you choose which one to live with. Named Views let you keep both — the raw response stays untouched, and you build as many lenses over it as you have audiences. Save them once and they belong to the endpoint, not to you, not to your laptop.

🦴 Dig Deeper

Paging exists because no sensible API hands you ten thousand records in one response — it protects the server’s memory and yours. The awkward part is always knowing when to stop, which is why responses carry a total count. Dobermann’s {{A8:PAGE}} variable reads that count and does the walking for you; see Pagination for the full set.

Check My Homework

To pass: Your filtered report, copied from the Console, pasted into the lesson and verified