Use case
Brand directories
A directory of government bodies, of companies in a sector, or of every brand in a country: a filtered list of cards from Lists alone, and the brand’s full page with colours, website and versions when a user opens it.
Endpoints
- GET /api/v1/brands
- GET /api/v1/brands/{slug}counts as a pull
- Products
- Lists, identity data
- Pulls
- One per card opened
- The list
- Not counted
The problem, and the fix
A directory needs correct names and current logos for thousands of brands, and gathering and updating them by hand never ends.
Lists give you pages of brands with filters, each card with name, country, sector, number of versions and logo image. For the full card, read the brand’s record when a user opens it, not for every card in the list.
Who it’s for
Government portals
A directory of government bodies with their official logos.
Sector directories
Banks, universities, or tech companies in a country.
Media and research
Pages about brands with their logos and identity history.
Try it
Change the filters and watch the list change with no pulls, then open a card to read the brand’s record: that is where a pull counts.
Build it
- 01
Get a key
Open a developer account in the console and make a key. The free plan is enough to start, and the key stays on your server in an environment variable.
- 02
The list page
Your filters are parameters in the address:
country,category,entityType,verified; number pages withpageCount. - 03
The card
From
itemsas they are, with the thumbnail. - 04
The brand’s page
Read the record when a user opens it, and keep it.
- 05
Refresh daily
Walk the list with
sort=updateduntil you reach what you already have.
A page of the directory
directory.js
const base = 'https://new.wasmenia.com';/** One page of a directory: Saudi brands with an SVG logo, newest first. */export async function directoryPage(page = 1) { const url = new URL('/api/v1/brands', base); url.searchParams.set('country', 'SA'); url.searchParams.set('has', 'svg'); url.searchParams.set('limit', '24'); // up to 50 url.searchParams.set('page', String(page)); const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.WASMENIA_API_KEY}` }, }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const { total, pageCount, items } = await res.json(); // pageCount is 0 when nothing matches. return { total, pageCount, items };}The daily refresh
refresh.js
// Once a day: re-read what changed since the last run, newest change first.const base = 'https://new.wasmenia.com';const headers = { Authorization: `Bearer ${process.env.WASMENIA_API_KEY}` };export async function refresh(since /* Date of the last run */) { for (let page = 1; page <= 500; page++) { const list = await fetch(`${base}/api/v1/brands?sort=updated&limit=50&page=${page}`, { headers }); if (!list.ok) throw new Error(`HTTP ${list.status}`); const { items, pageCount } = await list.json(); for (const card of items) { if (!(await db.records.has(card.slug))) continue; // never opened: nothing to refresh const res = await fetch(`${base}/api/v1/brands/${card.slug}`, { headers }); // a pull if (res.status === 402) return; // the month's pulls are used: try after X-Quota-Reset if (res.status === 404) { await db.records.delete(card.slug); continue; } if (!res.ok) throw new Error(`HTTP ${res.status}`); const brand = await res.json(); // A renamed brand answers under its new slug (308, followed by fetch). if (brand.slug !== card.slug) await db.records.rename(card.slug, brand.slug); await db.records.put(brand.slug, brand); if (new Date(brand.updatedAt) < since) return; // older than the last run: done } if (page >= pageCount) return; }}How many pulls it costs
The list, its pages and filters aren’t counted. Each card a user opens is one pull, or none if you have its record. A directory of 2,000 brands where 300 are opened a month costs 300 pulls the first month, then only those that changed.
Questions
Should I read every record up front?
No need. A card only needs the list, and the record when it opens. That way pulls go only to what your users see.
Where do the filter values come from?
Fixed values (entity type, order, files) are in the list reference (Arabic); sector slugs from list cards and archive pages.
Products it uses
Identity data
The name in both languages, other names, colours and their source, fonts and guidelines.
Lists
Pages of brands filtered by country, sector and type, never counted as pulls.
Build a directory page
Filters, pages, and what the card shows.