Developers & partners
Membership check API
A free JSON API that confirms whether someone is a current British Powerlifting member — for example to give BP members a discount at your gym, shop, event or online checkout. No sign-up or API key is needed.
It may only be used for the authorised uses, under the terms of use.
Quick start
Send at least two of a person's full name, email address and membership number:
curl -sS -X POST 'https://tools.britishpowerlifting.org/api/v1/membership-check' \
-H 'Content-Type: application/json' \
-d '{"first_name": "Jane", "last_name": "Smith", "membership_number": "GB123456"}'
You get back one of three answers, always with HTTP 200:
Active member
{"status": "active",
"membership_expires_on": "2027-03-31",
"matched_on": ["name", "membership_number"],
"data_as_of": "2026-10-05T01:14:09Z",
"data_may_be_stale": false,
"checked_at": "2026-10-05T09:30:00Z"}
Expired
{"status": "expired",
"membership_expires_on": "2026-08-31",
"matched_on": ["name", "membership_number"],
"data_as_of": "2026-10-05T01:14:09Z",
"data_may_be_stale": false,
"checked_at": "2026-10-05T09:30:00Z"}
Couldn't confirm
{"status": "not_found",
"membership_expires_on": null,
"matched_on": [],
"data_as_of": "2026-10-05T01:14:09Z",
"data_may_be_stale": false,
"checked_at": "2026-10-05T09:30:00Z"}
Version 1 is stable: we'll only ever add response fields, never remove or rename them,
so ignore fields you don't recognise. A breaking change would get a new /api/v2/membership-check address, with notice.
The old address, /membership-check/api/v1/check, still works but is deprecated.
Who can use this
You may use the membership checker and its API only to confirm that a person holds a current British Powerlifting membership, for one of these purposes:
- Running or organising a competition or event, to check that entrants are current members.
- A club or affiliated organisation confirming that its members or athletes hold British Powerlifting membership.
- A promotion, discount or benefit aimed at the British Powerlifting community, to confirm that someone qualifies for it.
- Coaching, officiating or access to a venue where current British Powerlifting membership is a requirement.
If your reason isn't listed, please contact British Powerlifting (opens in a new tab) before using this service.
No endorsement. Using this service does not mean British Powerlifting endorses, approves or partners with you, your organisation or your offers. Please don't say or suggest it does, or use our name or logo that way. Any promotion you run is your own responsibility.
How it works
It only confirms what you already know. You'll see whether the details you entered match a current British Powerlifting membership, and when it ends. It never returns any member information you didn't enter yourself: no names, email addresses, membership numbers, dates of birth or anything else from our records. Trying to find out more won't work, and breaks the terms of use.
Send at least two of these three:
- Full name:
first_nameandlast_name(both are needed, and they count as one). - Email address:
email - Membership number:
membership_number, with or without theGBprefix.
Every field you send must match the same membership record. Send three with one wrong, and the answer is not_found.
Matching ignores upper/lower case, accents, extra spaces, hyphens and apostrophes. Names must otherwise be exact: use the name the person registered with British Powerlifting, not a nickname or a shortened name.
Request
POSThttps://tools.britishpowerlifting.org/api/v1/membership-check
- Header
Content-Type: application/json, with aContent-Length(chunked bodies are refused). - The body must be a JSON object, under 2 KB, containing only the fields below.
- Always
POST, neverGET, so personal details never end up in URLs or server logs. - Checks are read-only. Set a client timeout of 5–10 seconds; every answer takes at least about a quarter of a second, by design.
| Field | Type | Notes |
|---|---|---|
| first_name | string ≤ 100 | Send with last_name |
| last_name | string ≤ 100 | Send with first_name |
| string ≤ 254 | Case-insensitive | |
| membership_number | string ≤ 30 | GB123456 or 123456 |
Response
200 OK is returned for every completed check, including when no member matched, so a miss isn't an error.
{
"status": "active",
"membership_expires_on": "2027-03-31",
"matched_on": ["name", "membership_number"],
"data_as_of": "2026-10-05T01:14:09Z",
"data_may_be_stale": false,
"checked_at": "2026-10-05T09:30:00Z"
}
| Field | Meaning |
|---|---|
| status |
active: a current member.expired: the details match a membership that has lapsed (the date is always given).not_found: we can't confirm a current membership matching every field you sent.
|
| membership_expires_on |
YYYY-MM-DD or null. For active, the last day the membership is valid
(null if we don't hold it). For expired, the day it expired. Always null for not_found.
|
| matched_on | Which of your fields were checked: name, email, membership_number. Never their stored values. Empty for not_found. |
| data_as_of | When our member records were last refreshed (UTC), or null if unknown. |
| data_may_be_stale | true if the records haven't been refreshed in the last 48 hours. |
| checked_at | When this check ran (UTC). |
Errors
Errors (other than a 404) return {"error": "<code>", "message": "<human-readable>"}. Branch on error, not on message.
| HTTP | error | Meaning |
|---|---|---|
| 400 | insufficient_details | Fewer than two of name / email / membership number. |
| 400 | incomplete_name | Only one of first_name / last_name was sent. |
| 400 | invalid_email · invalid_membership_number · invalid_field · field_too_long | A field is malformed, isn't a string, or is too long. |
| 400 | invalid_json · unknown_field | The body isn't a JSON object, or contains a field not listed above. |
| 405 | method_not_allowed | Not a POST (the response carries Allow: POST). |
| 413 | payload_too_large | The body is over 2 KB, or has no Content-Length. Checked before the content type. |
| 415 | unsupported_media_type | The body isn't sent as application/json. |
| 429 | rate_limited | Too many checks. Wait for the Retry-After header (currently 120 seconds). |
| 404 | — | The API is not currently available (an ordinary "page not found", not JSON). |
Rate limits
To protect members' details from anyone trying to guess them, every check counts towards several limits, shared with the web form:
- Per IP address: roughly 10 a minute, 60 an hour and 200 a day (IPv6 is counted per /64 network).
- Unmatched checks per IP address are limited much more tightly than successful ones.
- Per person: repeated unmatched checks against the same membership number, email address or name are limited, whoever sends them.
- Overall: a daily ceiling on all checks from everyone.
Responses don't carry rate-limit counters. Over a limit, you get a 429 with a Retry-After header:
wait at least that long, and never retry in a tight loop. Never automatically retry a not_found —
it counts against the stricter limits and the answer won't change. Exact limits may change without notice.
Check one person when you need to, e.g. when they claim a discount — not whole lists. For higher volumes, contact British Powerlifting (opens in a new tab).
Examples
Call the API from your server, not from a web browser. It deliberately doesn't allow cross-origin (CORS) requests, so JavaScript on your own website can't call it directly. Check from your backend, then pass the result to your page.
import requests
resp = requests.post(
"https://tools.britishpowerlifting.org/api/v1/membership-check",
json={"first_name": "Jane", "last_name": "Smith",
"membership_number": "GB123456"},
timeout=10,
)
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", "120"))
raise RuntimeError(f"Rate limited, retry in {wait}s")
resp.raise_for_status()
is_member = resp.json()["status"] == "active"
const resp = await fetch("https://tools.britishpowerlifting.org/api/v1/membership-check", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, membership_number }),
signal: AbortSignal.timeout(10_000),
});
if (resp.status === 429) {
const wait = Number(resp.headers.get("Retry-After") ?? 120);
throw new Error(`Rate limited, retry in ${wait}s`);
}
if (!resp.ok) throw new Error(`Check failed: ${resp.status}`);
const isMember = (await resp.json()).status === "active";
$ch = curl_init("https://tools.britishpowerlifting.org/api/v1/membership-check");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"email" => $email, "membership_number" => $number]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$body = json_decode(curl_exec($ch), true);
$isMember = curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200
&& ($body["status"] ?? null) === "active";
curl -sS -X POST 'https://tools.britishpowerlifting.org/api/v1/membership-check' \
-H 'Content-Type: application/json' \
-d '{"email": "[email protected]",
"membership_number": "GB123456"}'
Integrating: discounts, testing and what to tell customers
Offering a member discount
- At checkout or sign-up, ask for the customer's BP membership number plus their name or email — and tell them you'll check it with British Powerlifting.
- Your server calls the API.
- If
statusisactive, apply the discount. You can also checkmembership_expires_onis after the date of the service you're selling. - If it's
expiredornot_found, ask the customer to check their details or renew in Sport:80. Don't keep retrying variations.
Offering a discount doesn't make you a British Powerlifting partner. Don't present your offer as endorsed or approved by British Powerlifting.
Testing
There's no sandbox: the API checks real member records. Test with your own membership details (or a
colleague's, with their permission). Made-up example data always returns not_found and
still counts towards the stricter limits, so keep test runs small.
What to show your customer
| status | Suggested wording |
|---|---|
| active | “Thanks — your British Powerlifting membership is confirmed (valid until 31 March 2027).” |
| expired | “Your British Powerlifting membership ended on 31 August 2026. If you've just renewed, it can take up to a day to show.” |
| not_found | “We couldn't confirm your membership. Please check your details match your British Powerlifting membership exactly.” |
Data freshness
Member records are copied from the British Powerlifting membership system (Sport:80) every night, so a
renewal, a new membership or a lapse can take up to a day to show. Every response includes
data_as_of, so you can tell how fresh the answer is.
If a customer has just joined or renewed but gets not_found or expired, ask them to
show their membership in Sport:80, or try again the next day.
Records last updated 12 Nov 2026 at 02:03 GMT.
Acceptable use
- Only check people who have given you their details for an authorised purpose and expect you to verify their membership.
- Don't use the API to find out, guess or enumerate who is a member, to check lists of people, or to build marketing lists.
- Don't try to use the API to get any more information about a member. It only ever confirms what you sent.
- You're responsible for your own UK GDPR compliance. Tell people you'll check their membership with British Powerlifting, don't store results without the member's consent (or another clear lawful basis), keep only what you need for as long as you need it, and keep it secure.
- Don't resell or pass on results. A membership can lapse or renew at any time, so check again rather than relying on an old result.
- The service is provided "as is", with no guarantee of accuracy or availability, and may be changed or withdrawn at any time. We monitor usage and may block anyone misusing it.
The full terms of use apply. Questions? Contact British Powerlifting (opens in a new tab).