# Brand directories

> A list filtered by country, sector and type, with a card for each brand.

Source: https://new.wasmenia.com/en/developers/use-cases/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.

- `GET https://new.wasmenia.com/api/v1/brands`
- `GET https://new.wasmenia.com/api/v1/brands/{slug}`

- **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.

## Build it

### 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.

### The list page

Your filters are parameters in the address: `country`, `category`, `entityType`, `verified`; number pages with `pageCount`.

### The card

From `items` as they are, with the thumbnail.

### The brand’s page

Read the record when a user opens it, and keep it.

### Refresh daily

Walk the list with `sort=updated` until you reach what you already have.

## A page of the directory

`directory.js`

```javascript
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 };
}
```

```python
import os

import requests

def directory_page(page=1):
    res = requests.get(
        'https://new.wasmenia.com/api/v1/brands',
        params={'country': 'SA', 'has': 'svg', 'limit': 24, 'page': page},
        headers={'Authorization': f"Bearer {os.environ['WASMENIA_API_KEY']}"},
        timeout=10,
    )
    res.raise_for_status()
    data = res.json()
    return data['total'], data['pageCount'], data['items']
```

## The daily refresh

`refresh.js`

```javascript
// 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](https://new.wasmenia.com/developers/docs/reference/brands-list.md#brands-list) (Arabic); sector slugs from list cards and archive pages.

**Build a directory page**

Filters, pages, and what the card shows.
