# PPN 018 Payment Check

A company's statutory payment report read against the PPN 018 tests and the Fair Payment Code tiers.

PPN 018's two tests (95% within 60 days, 90% with an action plan; 45-day average) applied to the last two statutory reports, plus the Fair Payment Code tiers; meets / with an action plan / doesn't meet, with the figures.

Since 1 October 2025, a bidder for a central-government contract worth more than £5 million a year has to show it pays at least 95% of invoices within 60 days (90% with an action plan) and all its invoices within an average of 45 days (Procurement Policy Note 018); a bidder that can't is rejected. The figures that decide it are the ones large companies already publish every six months in their statutory payment-practices reports. This endpoint reads a company's last two reports against those two tests and says meets, meets with an action plan, or doesn't meet, with the figures and the period; and reads the same report against the Fair Payment Code's percentage tiers (Gold 95% within 30 days, Bronze 95% within 60). For a bidder checking itself before a bid, a buyer checking bidders, a prime checking subcontractors, or a bid tool showing it on every supplier. A company that doesn't report is said so.

- Price: 1 credit
- Page: https://meetkatalis.com/apis/ppn-018
- OpenAPI: https://meetkatalis.com/openapi.yaml

## Endpoints

- `GET /api/v1/ppn-018?company=…` — A company number or name

## Call it

```bash
curl -H "x-api-key: $KATALIS_API_KEY" \
  "https://meetkatalis.com/api/v1/ppn-018?company=00519500"
```

```javascript
const res = await fetch("https://meetkatalis.com/api/v1/ppn-018?company=00519500", {
  headers: { "x-api-key": process.env.KATALIS_API_KEY },
});
if (!res.ok) throw new Error(`Katalis ${res.status}: ${await res.text()}`);
console.log(await res.json());
```

```python
import os, requests

r = requests.get(
    "https://meetkatalis.com/api/v1/ppn-018?company=00519500",
    headers={"x-api-key": os.environ["KATALIS_API_KEY"]},
    timeout=60,
)
r.raise_for_status()
print(r.json())
```

## Example response (a real reply)

```json
{
  "company": { "name": "TESCO STORES LIMITED", "number": "00519500" },
  "reported": true,
  "ppn018": {
    "latest": { "period": { "start": "2026-03-02", "end": "2026-08-30", "filed": "2026-09-29" }, "within60": 95, "averageDays": 43, "tests": [ { "test": "At least 95% of invoices paid within 60 days (90% with an action plan)", "rule": "PPN 018", "figure": 95, "met": true }, { "test": "All invoices paid within an average of 45 days", "rule": "PPN 018", "figure": 43, "met": true } ], "result": "meets", "reading": "Meets both PPN 018 payment tests for the period ending 2026-08-30: 95% of invoices paid within 60 days (test: 95%) and an average of 43 days (test: 45 or fewer)." },
    "summary": "meets in both of its last two reports"
  },
  "fairPaymentCode": { "gold": { "test": "At least 95% of all UK invoices paid within 30 days", "figure": 13, "met": false }, "bronze": { "test": "At least 95% of invoices paid within 60 days", "figure": 95, "met": true }, "silver": { "met": null, "note": "The statutory report doesn't split invoices by supplier size, so this tier can't be judged from it." } }
}
```

## What you'd build with it

- **Bid teams.** Before the Procurement Specific Questionnaire: do our last two reports pass, and by how much?
- **Bid and tender software.** The PPN 018 result on every supplier profile, by company number.
- **Primes and buyers.** Which subcontractors and bidders would fail the payment tests.

## Why this one

- **The tests as published, not a score.** 95% within 60 days, 90% with an action plan, a 45-day average: the words of PPN 018, applied to the company's own filed figures.
- **Two reports, as the questionnaire asks.** The latest and the one before, each judged, and one line for both.
- **Honest about scope.** The Fair Payment Code's Silver tier can't be judged from the statutory report, and the reply says so rather than guessing.

## Conventions

- Auth: `x-api-key: kat_live_…` on every request (free key at https://meetkatalis.com/developers: a verified email and mobile, 100 credits a month, no card).
- One credit balance across every API; each endpoint's price is on its page and in `x-credits` in https://meetkatalis.com/openapi.yaml.
- Keys are camelCase everywhere. A registered company is `company: { name, number, status, statusDetail?, filingsOverdue? }` on the register endpoints; the enrichment family (enrich, score, assess, brand-kit, ai-visibility, tender-match) keeps `business` / `profile` with `companyNumber`.
- Every error has `code` and `requestId`; every reply an `x-request-id` header; a charged reply `x-credits-remaining`; a reply that gave credits back `x-credits-refunded`. Missing = 404 with `query`.
- Burst ceiling 60 requests a minute per key (429, `Retry-After`, no credits used). A failure on our side (5xx) gives the call's credits back.
- Company data contains public sector information licensed under the Open Government Licence v3.0.

Provided "as is"; results come from public sources and, in places, AI, and may be inaccurate or out of date; not legal, professional or credit advice. Terms: https://meetkatalis.com/apis/terms