{"openapi":"3.1.0","info":{"title":"A/B Bucketing API","version":"1.0.0","description":"Deterministic A/B testing and feature-flag assignment — no database, no stored state. The bucket endpoint hashes a key (a user id, session or device) into a stable bucket from 0 to N-1 that never changes for that key, and can decide whether the key is inside a percentage rollout; because the decision is monotonic, raising the percentage only ever adds users, so a gradual ramp-up is sticky and no one flips back out. The variant endpoint assigns one of several weighted variants — a simple control/treatment split or any multivariate test — consistently for the same key, honouring custom weights. Mixing in an experiment name keeps independent experiments independent, and because the same inputs always produce the same answer, your client and server (and any edge function) agree on the assignment without any coordination or lookups. Hashing is FNV-1a with an avalanche mix, giving uniform, stable buckets across languages and machines. It runs entirely locally, so it is instant, deterministic and private. Ideal for feature flags and gradual rollouts, A/B and multivariate experiments, canary releases, holdouts and kill-switches, and consistent UI bucketing across web and mobile. Pure local computation — no key, no third-party service, instant. Live, nothing stored. 3 endpoints. This assigns experiments deterministically; to test whether a result is statistically significant use a statistics API.","contact":{"name":"PremiumApi","url":"https://www.oanor.com/by/premiumapi"}},"servers":[{"url":"https://api.oanor.com/abtest-api","description":"oanor gateway"}],"tags":[{"name":"A/B"},{"name":"Meta"}],"components":{"securitySchemes":{"oanorKey":{"type":"apiKey","in":"header","name":"x-oanor-key","description":"Get your key at https://www.oanor.com/developer/keys"}}},"security":[{"oanorKey":[]}],"paths":{"/v1/bucket":{"get":{"operationId":"get_v1_bucket","tags":["A/B"],"summary":"Bucket a key","description":"","parameters":[{"name":"key","in":"query","required":true,"description":"The unit to bucket — user id, session, etc.","schema":{"type":"string"},"example":"user-42"},{"name":"experiment","in":"query","required":false,"description":"Experiment/salt name so experiments are independent","schema":{"type":"string"},"example":"exp1"},{"name":"buckets","in":"query","required":false,"description":"Number of buckets (default 100)","schema":{"type":"string"}},{"name":"percentage","in":"query","required":false,"description":"If set, also return enabled = inside this % rollout","schema":{"type":"string"},"example":"50"}],"security":[{"oanorKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"data":{"key":"user-42","hash":2066987872,"ratio":0.481258,"bucket":48,"buckets":100,"enabled":true,"experiment":"exp1","percentage":50},"meta":{"timestamp":"2026-06-03T09:25:01.927Z","request_id":"fe553bc6-79e1-40ca-b8dd-3fb4a6690d77"},"status":"ok","message":"Bucket a key","success":true}}}},"401":{"description":"Missing or invalid x-oanor-key header"},"402":{"description":"Active subscription required"},"429":{"description":"Rate-limit or monthly quota reached"},"502":{"description":"Upstream did not respond"}}}},"/v1/variant":{"get":{"operationId":"get_v1_variant","tags":["A/B"],"summary":"Assign a variant","description":"","parameters":[{"name":"key","in":"query","required":true,"description":"The unit","schema":{"type":"string"},"example":"user-42"},{"name":"experiment","in":"query","required":false,"description":"Experiment/salt name","schema":{"type":"string"},"example":"cta"},{"name":"variants","in":"query","required":true,"description":"JSON array of {name,weight}, \"a:50,b:50\", or \"a,b,c\"","schema":{"type":"string"},"example":"control:70,treatment:30"}],"security":[{"oanorKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"data":{"key":"user-42","hash":2472422117,"variant":"control","weights":[{"name":"control","share":0.7,"weight":70},{"name":"treatment","share":0.3,"weight":30}],"experiment":"cta"},"meta":{"timestamp":"2026-06-03T09:25:02.030Z","request_id":"55d3d8cd-a193-4303-8f1c-e90378ac7cc5"},"status":"ok","message":"Assign a variant","success":true}}}},"401":{"description":"Missing or invalid x-oanor-key header"},"402":{"description":"Active subscription required"},"429":{"description":"Rate-limit or monthly quota reached"},"502":{"description":"Upstream did not respond"}}}},"/v1/meta":{"get":{"operationId":"get_v1_meta","tags":["Meta"],"summary":"Spec","description":"","parameters":[],"security":[{"oanorKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"data":{"name":"A/B Bucketing API","notes":"Hashing is FNV-1a (32-bit) with an avalanche mix, so buckets are uniform and stable across languages and machines. Bucketing and variant assignment use independent hash domains. This assigns experiments; to test whether a result is statistically significant use a statistics API. Nothing is stored.","version":"v1","endpoints":[{"path":"/v1/bucket","params":{"key":"the unit to bucket — user id, session, etc. (required)","buckets":"number of buckets (default 100)","experiment":"experiment/salt name so experiments are independent","percentage":"if set, also return enabled = inside this % rollout"},"returns":"the stable bucket (and rollout decision)"},{"path":"/v1/variant","params":{"key":"the unit (required)","variants":"JSON array of {name,weight}, \"a:50,b:50\", or \"a,b,c\" (equal weights)","experiment":"experiment/salt name"},"returns":"the assigned variant and the weight shares"},{"path":"/v1/meta","params":[],"returns":"this document"}],"description":"Deterministic A/B testing and feature-flag assignment — no database, no state. The bucket endpoint hashes a key (a user id, session or device) into a stable bucket (0…N-1) that never changes for the same key, and decides whether the key is inside a percentage rollout; raising the percentage only ever adds users, so a gradual ramp-up is sticky. The variant endpoint assigns one of several weighted variants (control/treatment, or any multivariate split) consistently for the same key. Mix in an experiment name so independent experiments bucket independently. The same inputs always give the same answer everywhere, so client and server agree without coordination. Pure local, no key."},"meta":{"timestamp":"2026-06-03T09:25:02.114Z","request_id":"4d0d62b0-9f9e-40ef-b864-05d66529f6e6"},"status":"ok","message":"Meta","success":true}}}},"401":{"description":"Missing or invalid x-oanor-key header"},"402":{"description":"Active subscription required"},"429":{"description":"Rate-limit or monthly quota reached"},"502":{"description":"Upstream did not respond"}}}}},"x-oanor-pricing":[{"slug":"free","name":"Free","price_cents_month":0,"monthly_call_quota":3035,"rps_limit":2,"hard_limit":true},{"slug":"starter","name":"Starter","price_cents_month":455,"monthly_call_quota":12550,"rps_limit":8,"hard_limit":true},{"slug":"pro","name":"Pro","price_cents_month":2445,"monthly_call_quota":176500,"rps_limit":20,"hard_limit":true},{"slug":"mega","name":"Mega","price_cents_month":6245,"monthly_call_quota":925000,"rps_limit":50,"hard_limit":true}],"x-oanor-marketplace-url":"https://www.oanor.com/api/abtest-api"}