# Producten

GET /products: wanneer een product verschijnt, de filters, het antwoord en de velden.

Sectie: PIM · API
Canonieke URL: https://docs.omniretailconnect.nl/pim/api/producten

## Producten ophalen

`GET /products` — De producten die je mag verkopen, met de aanbiedingen van de leveranciers.

Dit adres geeft de producten die voor jouw bedrijf beschikbaar zijn, met per product de gegevens, de status en de aanbiedingen van leveranciers. Het antwoord is een pagina met producten. Hoe je alle pagina's doorloopt, staat bij [Synchroniseren](https://docs.omniretailconnect.nl/pim/api/synchroniseren.md).

**Eerste twee producten ophalen · curl**

```bash
# Zet je token in een omgevingsvariabele. Zet het nooit in een script dat je deelt.
export PIM_API_TOKEN='<jouw-token>'

# -g laat curl de blokhaken in filter[...] ongemoeid.
curl -sS -g "https://pim.internetservicebureau.nl/api/v1/products?per_page=2" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $PIM_API_TOKEN"
```

**Eerste twee producten ophalen · JavaScript**

```javascript
// Alleen in code op je eigen server: zet het token nooit in browsercode.
// Top-level await werkt in een ES-module (een .mjs-bestand of "type": "module").
const token = process.env.PIM_API_TOKEN;

const response = await fetch('https://pim.internetservicebureau.nl/api/v1/products?per_page=2', {
  headers: {
    Accept: 'application/json',
    Authorization: `Bearer ${token}`,
  },
});

// Een fout is ook JSON: {"message": "..."}
console.log(response.status, await response.json());
```

**Eerste twee producten ophalen · PHP**

```php
<?php

// Het token komt uit de omgeving, niet uit de code.
$token = getenv('PIM_API_TOKEN');

$curl = curl_init('https://pim.internetservicebureau.nl/api/v1/products?per_page=2');
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Accept: application/json',
        "Authorization: Bearer {$token}",
    ],
]);

$body = json_decode(curl_exec($curl), true);

// Een fout is ook JSON: {"message": "..."}
echo curl_getinfo($curl, CURLINFO_RESPONSE_CODE), PHP_EOL;
print_r($body);
```

**Eerste twee producten ophalen · Python**

```python
import json
import os
import urllib.error
import urllib.request

# Het token komt uit de omgeving, niet uit de code.
token = os.environ["PIM_API_TOKEN"]

request = urllib.request.Request(
    "https://pim.internetservicebureau.nl/api/v1/products?per_page=2",
    headers={
        "Accept": "application/json",
        "Authorization": f"Bearer {token}",
    },
)

try:
    with urllib.request.urlopen(request) as response:
        print(response.status, json.load(response))
except urllib.error.HTTPError as error:
    # Een fout is ook JSON: {"message": "..."}
    print(error.code, json.load(error))
```

## Wanneer een product verschijnt

Een product staat in de lijst als dit allemaal klopt:

- Het product heeft een EAN.
- Je hebt een actief klantartikel voor dit product. Een verwijderd of op inactief gezet artikel telt niet. Is het artikel in je eigen kassa of ERP op inactief gezet, dan blijft het ook weg.
- Er is minstens één actief aanbod dat je mag gebruiken. Of dat aanbod voor jou geldt, hangt af van je abonnement, licenties en merkrechten, zie [Hoe het werkt](https://docs.omniretailconnect.nl/pim/aan-de-slag/hoe-het-werkt.md#mag-je-een-aanbod-gebruiken).

Er is één uitzondering. Heeft een artikel uit je eigen kassa of ERP een eigen voorraad groter dan nul, dan blijft het in de lijst staan, ook als het aanbod van de leverancier uitloopt of niet verkrijgbaar is. Dat aanbod moet je dan nog wel mogen gebruiken.

Valt een product uit de lijst, dan krijg je daar geen bericht van. Het is er gewoon niet meer. Meer daarover bij [Synchroniseren](https://docs.omniretailconnect.nl/pim/api/synchroniseren.md#verdwenen-producten).

## Parameters

*Queryparameters van GET /products*

| Naam | Type | Verplicht | Omschrijving |
| --- | --- | --- | --- |
| `page` | integer | Optioneel | Het paginanummer, te beginnen bij 1. Volg liever `links.next`. |
| `per_page` | integer | Optioneel | Het aantal producten per pagina. Standaard 100, maximaal 1000. Geef je 0 of minder, of meer dan 1000, dan krijg je geen foutmelding maar 100 producten. |
| `limit` | integer | Optioneel | Hetzelfde als `per_page`. Geef je allebei op, dan wint `per_page`. |
| `filter[updated_at]` | string | Optioneel | Alleen producten die zijn gewijzigd op of na (of voor) een tijdstip. Zie hieronder. |
| `filter[stock]` | string | Optioneel | Filter op je eigen voorraad uit je kassa of ERP. Zie hieronder. |
| `filter[nut_plas_number]` | string | Optioneel | Een of meer NUT- of PLAS-nummers, gescheiden door een komma. Zoekt in de aanbiedingen die je mag gebruiken. |
| `filter[ean13]` | string | Optioneel | Eén EAN. Zoekt op de EAN van het product en op de EAN van de aanbiedingen. |
| `filter[eancode]` | string | Optioneel | Hetzelfde als `filter[ean13]`. |

Het antwoord zegt niet hoeveel producten er in totaal zijn. Je weet dat je op de laatste pagina bent als `links.next` `null` is.

## Filters

### Gewijzigd sinds (updated_at)

Met dit filter vraag je alleen de producten op die zijn gewijzigd. Zet er een voorvoegsel voor, zoals `>=`, en daarachter een tijdstip.

*Voorvoegsels voor filter\[updated_at\] en filter\[stock\]*

| Voorvoegsel | In de URL | Betekenis |
| --- | --- | --- |
| `>=` | `%3E%3D` | Groter dan of gelijk aan. Dit gebruik je om wijzigingen op te halen. |
| `>` | `%3E` | Groter dan. |
| `<=` | `%3C%3D` | Kleiner dan of gelijk aan. |
| `<` | `%3C` | Kleiner dan. |
| `<>` | `%3C%3E` | Ongelijk aan. |
| `=` | `%3D` | Gelijk aan. Dit is ook wat je krijgt zonder voorvoegsel. |

De tekens `>`, `<` en `=` kunnen niet zomaar in een URL. Schrijf ze als `%3E`, `%3C` en `%3D`. Dus `>=` wordt `%3E%3D`. Functies als `http_build_query`, `URLSearchParams` en de `params` van `requests` doen dat voor je. Bij curl met `-g` schrijf je het zelf.

Het tijdstip mag in een van deze vormen:

- `2026-03-12T10:00:00`
- `2026-03-12 10:00:00`, met de spatie als `%20` in de URL
- `2026-03-12`, dat telt als middernacht aan het begin van die dag

> **Waarschuwing — Geef de tijd in Nederlandse tijd:** De PIM rekent met Nederlandse tijd (`Europe/Amsterdam`). Een tijdzone achter het tijdstip, zoals `Z` of `+01:00`, wordt niet omgerekend. De kloktijd telt. Geef dus de tijd zoals die in Nederland op de klok staat. De tijdstippen in het antwoord (`changed_at`) hebben de Nederlandse tijdzone al.

Wat telt als wijziging? Een product komt mee als er iets veranderde in zijn inhoud: de naam, beschrijving, afbeeldingen, prijs of voorraad. Dat kan gaan om het product zelf, om een aanbod dat je mag gebruiken, of om je eigen klantartikel. Bij een artikel uit je eigen kassa of ERP tellen ook een wijziging van je eigen voorraad en een wijziging van de status van het aanbod mee.

Alle producten die sinds 12 maart 2026 om 10:00 uur zijn gewijzigd:

**Wijzigingen sinds een tijdstip**

```bash
curl -sS -g "https://pim.internetservicebureau.nl/api/v1/products?filter[updated_at]=%3E%3D2026-03-12T10:00:00" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $PIM_API_TOKEN"
```

Zonder voorvoegsel zoekt de PIM op precies dat tijdstip. Dat levert bijna altijd een lege lijst op. Gebruik daarom een voorvoegsel.

### Eigen voorraad (stock)

Dit filter werkt op `stock` bovenaan het product: je eigen voorraad uit je kassa of ERP. Het gebruikt dezelfde voorvoegsels als hierboven. De voorraad van leveranciers telt niet mee. Producten zonder eigen voorraad komen er niet door, ook niet bij `<=5`.

De waarde moet een getal zijn, eventueel met een voorvoegsel. Anders krijg je `422`, zie [Foutmeldingen](https://docs.omniretailconnect.nl/pim/referentie/foutmeldingen.md).

**Eigen voorraad van 5 of meer**

```bash
curl -sS -g "https://pim.internetservicebureau.nl/api/v1/products?filter[stock]=%3E%3D5" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $PIM_API_TOKEN"
```

### Eén product opzoeken (ean13)

**Zoeken op EAN**

```bash
curl -sS -g "https://pim.internetservicebureau.nl/api/v1/products?filter[ean13]=8710000000001" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $PIM_API_TOKEN"
```

Een onbekend filter geeft een `400`. Je kunt filters combineren: ze gelden dan allemaal tegelijk.

## Het antwoord

Het antwoord is JSON met `data`, `links` en `meta`. Er is geen totaal. `links.last` is altijd `null`. Hieronder één product. De waarden zijn verzonnen.

**Antwoord van GET /products**

```json
{
  "data": [
    {
      "ean13": "8710000000001",
      "reference": "REF-1",
      "name": "Voorbeeldmerk Voorbeeld Vitamine C - 90cp",
      "original_name": "Voorbeeld Vitamine C",
      "nut_plas_number": null,
      "brand": "Voorbeeldmerk",
      "vat": 9,
      "category": null,
      "content": "90cp",
      "unit_value": 90,
      "unit_type": "cp",
      "short_description": null,
      "description": "<p>Voorbeeldomschrijving.</p>",
      "ingredients": [],
      "specifications": [],
      "attributes": [],
      "nutrition": [],
      "images": [
        "https://pim.internetservicebureau.nl/storage/img/voorbeeld.jpg"
      ],
      "status": "active",
      "eol_date": null,
      "field_sources": {
        "name": "Voorbeeldleverancier",
        "brand": "Voorbeeldleverancier",
        "content": "Voorbeeldleverancier",
        "short_description": "Voorbeeldleverancier",
        "description": "Voorbeeldleverancier",
        "images": "Voorbeeldleverancier"
      },
      "changed_at": {
        "name": "2026-03-01T09:00:00+01:00",
        "short_description": null,
        "description": null,
        "images": null,
        "price": "2026-03-12T11:30:00+01:00",
        "stock": "2026-03-12T11:30:00+01:00"
      },
      "stock": 7,
      "suppliers": [
        {
          "licence": "",
          "supplier": "Voorbeeldleverancier",
          "sku": "V-1",
          "name": "Voorbeeld Vitamine C",
          "nut_plas_number": null,
          "status": "active",
          "stock": 40,
          "price": 8.5,
          "price_incl": "9.27",
          "advice_price": 14.95,
          "wholesale_price": null,
          "category": null,
          "ingredients": [],
          "attributes": [],
          "nutrition": [],
          "description": null,
          "clean_description": null,
          "images": [
            "https://pim.internetservicebureau.nl/storage/img/voorbeeld.jpg"
          ]
        }
      ]
    }
  ],
  "links": {
    "first": "https://pim.internetservicebureau.nl/api/v1/products?page=1",
    "last": null,
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "current_page_url": "https://pim.internetservicebureau.nl/api/v1/products?page=1",
    "from": 1,
    "path": "https://pim.internetservicebureau.nl/api/v1/products",
    "per_page": 100,
    "to": 1
  }
}
```

### Velden van een product

*Velden van een product*

| Veld | Type | Omschrijving |
| --- | --- | --- |
| `ean13` | string | De EAN van het product. Hieraan herken je een product. Producten zonder EAN komen niet in de lijst. |
| `reference` | string\|null | Referentie van het product. |
| `name` | string\|null | De volledige naam: merk, naam en inhoud achter elkaar, zoals `Voorbeeldmerk Voorbeeld Vitamine C - 90cp`. |
| `original_name` | string\|null | De naam van het centrale product, zonder merk en inhoud erbij. |
| `nut_plas_number` | string\|null | Het NUT- of PLAS-nummer, uit het hoofdaanbod of anders uit een ander aanbod dat je mag gebruiken. |
| `brand` | string\|null | De merknaam in een vaste schrijfwijze. |
| `vat` | integer | Het btw-percentage. Is er niets vastgelegd, dan is het `21`. |
| `category` | string\|null | De categorie van het hoofdaanbod. |
| `content` | string\|null | De inhoud als tekst, bijvoorbeeld `90cp`. Zonder bekende inhoud is het `1st`. |
| `unit_value` | integer\|number\|null | Het getal uit de inhoud, bijvoorbeeld `90`. |
| `unit_type` | string\|null | De eenheid uit de inhoud, bijvoorbeeld `cp` of `st`. |
| `short_description` | string\|null | Korte beschrijving. Kan HTML bevatten. |
| `description` | string\|null | Beschrijving. Kan HTML bevatten. |
| `ingredients` | array | Ingrediënten. Leeg als er geen zijn: `[]`. |
| `specifications` | array | Specificaties. Leeg als er geen zijn: `[]`. |
| `attributes` | array | Kenmerken van het hoofdaanbod. Leeg als er geen zijn: `[]`. |
| `nutrition` | array | Voedingswaarden. Leeg als er geen zijn: `[]`. |
| `images` | array | Afbeeldingen. Meestal een lijst met volledige internetadressen. Een afbeelding kan ook een object zijn met `url` en een markering voor de hoofdafbeelding. |
| `status` | string | De status van het product: `active`, `eol` of `unavailable`. |
| `eol_date` | string\|null | De einddatum (ISO 8601) van een product dat door je eigen voorraad in de lijst blijft, als de leverancier die kent. Anders `null`. |
| `field_sources` | object | Per veld waar de inhoud vandaan komt: de naam van een leverancier of `Manual`. Velden: `name`, `brand`, `content`, `short_description`, `description`, `images`. |
| `changed_at` | object | Per veld het tijdstip van de laatste wijziging (ISO 8601) of `null`. Velden: `name`, `short_description`, `description`, `images`, `price`, `stock`. |
| `stock` | integer\|null | Je eigen voorraad uit je kassa of ERP. `null` voor artikelen die daar niet vandaan komen. |
| `suppliers` | array | De aanbiedingen van leveranciers die je mag gebruiken. Zie hieronder. |

### Velden van een aanbod in suppliers

*Velden van een aanbod*

| Veld | Type | Omschrijving |
| --- | --- | --- |
| `licence` | string | De licentie die voor dit product nodig is. Een lege tekst als er geen nodig is. |
| `supplier` | string\|null | De naam van de leverancier. |
| `sku` | string\|null | Het artikelnummer van de leverancier. |
| `name` | string\|null | De naam zoals de leverancier die geeft. |
| `nut_plas_number` | string\|null | Het NUT- of PLAS-nummer van dit aanbod. |
| `status` | string | De status van het aanbod. Hier staan alleen actieve aanbiedingen, dus `active`. |
| `stock` | integer | De voorraad bij de leverancier. |
| `price` | number | De prijs van de leverancier, zonder btw. |
| `price_incl` | string | Dezelfde prijs met btw, op twee decimalen, als tekst. |
| `advice_price` | number\|null | De adviesprijs, als de leverancier die doorgeeft. |
| `wholesale_price` | number\|null | De groothandelsprijs, als de leverancier die doorgeeft. |
| `category` | string\|null | De categorie bij dit aanbod. |
| `ingredients` | array | Ingrediënten volgens de leverancier. |
| `attributes` | array | Kenmerken volgens de leverancier. |
| `nutrition` | array | Voedingswaarden volgens de leverancier. |
| `description` | string\|null | De beschrijving van de leverancier. Kan HTML bevatten. |
| `clean_description` | string\|null | De beschrijving van de leverancier zonder opmaak. |
| `images` | array | De afbeeldingen van de leverancier, als volledige internetadressen. |

Heeft een product voor een veld geen waarde, dan staat het veld er toch, met `null` of een lege lijst.

> **Opmerking — Geen eigen prijs bij Producten:** Dit adres toont de prijzen van de leveranciers. Jouw eigen prijs en voorraad per klantartikel staan bij [Mijn producten](https://docs.omniretailconnect.nl/pim/api/mijn-producten.md).
