# Lists

> Pages of brands with filters, and a card for each.

Source: https://new.wasmenia.com/en/developers/products/list

Pages of brands with filters that combine: country, sector, industry, entity type, government or private, verified, which files it has, the identity’s year and a public collection, in the order you choose. Every brand comes as a card ready to show, and lists are never counted as pulls.

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

- **Counts as a pull**: No
- **Page size**: Up to 50 brands, 500 pages
- **Limit**: 60 a minute without a key
- **An invalid value**: Ignored, as if not sent

## What you get

### Filters that combine

Country, sector, entity type, verification and files together, and several countries separated by commas.

### An order you choose

Newest, recently updated, most downloaded, or alphabetical.

### A card ready to show

Slug, name, country, sector, verification, number of versions and thumbnail.

### Pages you can count

`total` and `pageCount` to number pages from the first request.

### A copy of your own

Walk every page to build a list and a search of your own, with no pulls.

### Daily refresh

`sort=updated` gives what changed first, so you stop when you reach what you have.

## Filters

| Parameter | Example |
| --- | --- |
| `country` | `SA`, or `SA,AE` |
| `category` | A sector’s slug, including those under it |
| `entityType` | `government`, `private_company`… |
| `sector` | `government` or `private` |
| `verified` | `1` for verified brands |
| `has` | `svg`, `png`, `pdf` or `guidelines` |
| `collection` | A public collection’s slug |
| `sort` | `newest`, `updated`, `downloads`, `alpha_ar`… |

An invalid value is ignored as if it wasn’t sent. Every parameter is in the [list reference](https://new.wasmenia.com/developers/docs/reference/brands-list.md#brands-list) (Arabic).

## How to use 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.

### Build the address from the filters

Each filter is a parameter in the address; leave out what you don’t need.

### Show the cards

Each item in `items` is enough for a card: don’t read the record for every card.

### Number the pages

`page` and `pageCount`, and `total` for the number of results.

### Read the record when a card opens

For colours, website and versions: one pull when the user opens it.

## A page of a 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']
```

## Walking the whole list

`walk.js`

```javascript
// Every card of the archive, page by page. Lists are not pulls.
const base = 'https://new.wasmenia.com';
const headers = { Authorization: `Bearer ${process.env.WASMENIA_API_KEY}` };
const sleep = (s) => new Promise((r) => setTimeout(r, s * 1000));

export async function* allBrands(filters = {}) {
  for (let page = 1; ; page++) {
    const url = new URL('/api/v1/brands', base);
    for (const [k, v] of Object.entries({ ...filters, limit: 50, page }))
      url.searchParams.set(k, String(v));
    const res = await fetch(url, { headers });
    if (res.status === 429) {
      await sleep(Number(res.headers.get('Retry-After') ?? 60));
      page--; // the same page again
      continue;
    }
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const { items, pageCount } = await res.json();
    yield* items; // { slug, name, country, category, verified, versions, thumbnailUrl, url }
    if (page >= pageCount || page >= 500) return;
  }
}

for await (const brand of allBrands({ country: 'SA' })) {
  await db.cards.upsert(brand.slug, brand);
}
```

`walk.py`

```python
import os
import time

import requests

BASE = 'https://new.wasmenia.com'
HEADERS = {'Authorization': f"Bearer {os.environ['WASMENIA_API_KEY']}"}

def all_brands(**filters):
    page = 1
    while True:
        res = requests.get(
            f'{BASE}/api/v1/brands',
            params={**filters, 'limit': 50, 'page': page},
            headers=HEADERS,
            timeout=10,
        )
        if res.status_code == 429:
            time.sleep(int(res.headers.get('Retry-After', 60)))
            continue
        res.raise_for_status()
        data = res.json()
        yield from data['items']
        if page >= data['pageCount'] or page >= 500:
            return
        page += 1
```

## Questions

### The list is longer than the page limit

One set of filters walks at most 25,000 brands. Split the walk by a filter: country by country, or sector by sector.

### Does a card have the brand’s colours?

No. The card is for the list; colours, website and versions are in the record.

### What is `category` on a card?

The sector’s name for display. To filter, use the sector’s slug in the `category` parameter.

**A copy of the archive of your own**

The whole list with no pulls, the record when you need it, and a daily refresh.
