curl --request POST \
--url https://api.unprice.dev/v1/usage/consume \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"idempotencyKey": "idem_123",
"eventSlug": "tokens_used",
"properties": {
"amount": 1
},
"featureSlug": "tokens",
"id": "evt_123",
"customerId": "cus_123",
"timestamp": 1741454800000
}
'import requests
url = "https://api.unprice.dev/v1/usage/consume"
payload = {
"idempotencyKey": "idem_123",
"eventSlug": "tokens_used",
"properties": { "amount": 1 },
"featureSlug": "tokens",
"id": "evt_123",
"customerId": "cus_123",
"timestamp": 1741454800000
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
idempotencyKey: 'idem_123',
eventSlug: 'tokens_used',
properties: {amount: 1},
featureSlug: 'tokens',
id: 'evt_123',
customerId: 'cus_123',
timestamp: 1741454800000
})
};
fetch('https://api.unprice.dev/v1/usage/consume', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.unprice.dev/v1/usage/consume",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'idempotencyKey' => 'idem_123',
'eventSlug' => 'tokens_used',
'properties' => [
'amount' => 1
],
'featureSlug' => 'tokens',
'id' => 'evt_123',
'customerId' => 'cus_123',
'timestamp' => 1741454800000
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.unprice.dev/v1/usage/consume"
payload := strings.NewReader("{\n \"idempotencyKey\": \"idem_123\",\n \"eventSlug\": \"tokens_used\",\n \"properties\": {\n \"amount\": 1\n },\n \"featureSlug\": \"tokens\",\n \"id\": \"evt_123\",\n \"customerId\": \"cus_123\",\n \"timestamp\": 1741454800000\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.unprice.dev/v1/usage/consume")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"idempotencyKey\": \"idem_123\",\n \"eventSlug\": \"tokens_used\",\n \"properties\": {\n \"amount\": 1\n },\n \"featureSlug\": \"tokens\",\n \"id\": \"evt_123\",\n \"customerId\": \"cus_123\",\n \"timestamp\": 1741454800000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unprice.dev/v1/usage/consume")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"idempotencyKey\": \"idem_123\",\n \"eventSlug\": \"tokens_used\",\n \"properties\": {\n \"amount\": 1\n },\n \"featureSlug\": \"tokens\",\n \"id\": \"evt_123\",\n \"customerId\": \"cus_123\",\n \"timestamp\": 1741454800000\n}"
response = http.request(request)
puts response.read_body{
"allowed": true,
"state": "processed",
"idempotencyStatus": "already_reported",
"rejectionReason": "LIMIT_EXCEEDED",
"message": "Limit exceeded for meter meter_123"
}{
"error": {
"code": "BAD_REQUEST",
"docs": "https://docs.unprice.dev/api-reference/errors/code/BAD_REQUEST",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "UNAUTHORIZED",
"docs": "https://docs.unprice.dev/api-reference/errors/code/UNAUTHORIZED",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "FORBIDDEN",
"docs": "https://docs.unprice.dev/api-reference/errors/code/FORBIDDEN",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "NOT_FOUND",
"docs": "https://docs.unprice.dev/api-reference/errors/code/NOT_FOUND",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "CONFLICT",
"docs": "https://docs.unprice.dev/api-reference/errors/code/CONFLICT",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "PRECONDITION_FAILED",
"docs": "https://docs.unprice.dev/api-reference/errors/code/PRECONDITION_FAILED",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "PAYLOAD_TOO_LARGE",
"docs": "https://docs.unprice.dev/api-reference/errors/code/PAYLOAD_TOO_LARGE",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "TOO_MANY_REQUESTS",
"docs": "https://docs.unprice.dev/api-reference/errors/code/TOO_MANY_REQUESTS",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "INTERNAL_SERVER_ERROR",
"docs": "https://docs.unprice.dev/api-reference/errors/code/INTERNAL_SERVER_ERROR",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}ingest raw event synchronously for a feature
Validate and synchronously ingest a raw event for one feature slug. This is useful when you want to enforce exact limits from a ingestion.
curl --request POST \
--url https://api.unprice.dev/v1/usage/consume \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"idempotencyKey": "idem_123",
"eventSlug": "tokens_used",
"properties": {
"amount": 1
},
"featureSlug": "tokens",
"id": "evt_123",
"customerId": "cus_123",
"timestamp": 1741454800000
}
'import requests
url = "https://api.unprice.dev/v1/usage/consume"
payload = {
"idempotencyKey": "idem_123",
"eventSlug": "tokens_used",
"properties": { "amount": 1 },
"featureSlug": "tokens",
"id": "evt_123",
"customerId": "cus_123",
"timestamp": 1741454800000
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
idempotencyKey: 'idem_123',
eventSlug: 'tokens_used',
properties: {amount: 1},
featureSlug: 'tokens',
id: 'evt_123',
customerId: 'cus_123',
timestamp: 1741454800000
})
};
fetch('https://api.unprice.dev/v1/usage/consume', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.unprice.dev/v1/usage/consume",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'idempotencyKey' => 'idem_123',
'eventSlug' => 'tokens_used',
'properties' => [
'amount' => 1
],
'featureSlug' => 'tokens',
'id' => 'evt_123',
'customerId' => 'cus_123',
'timestamp' => 1741454800000
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.unprice.dev/v1/usage/consume"
payload := strings.NewReader("{\n \"idempotencyKey\": \"idem_123\",\n \"eventSlug\": \"tokens_used\",\n \"properties\": {\n \"amount\": 1\n },\n \"featureSlug\": \"tokens\",\n \"id\": \"evt_123\",\n \"customerId\": \"cus_123\",\n \"timestamp\": 1741454800000\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.unprice.dev/v1/usage/consume")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"idempotencyKey\": \"idem_123\",\n \"eventSlug\": \"tokens_used\",\n \"properties\": {\n \"amount\": 1\n },\n \"featureSlug\": \"tokens\",\n \"id\": \"evt_123\",\n \"customerId\": \"cus_123\",\n \"timestamp\": 1741454800000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unprice.dev/v1/usage/consume")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"idempotencyKey\": \"idem_123\",\n \"eventSlug\": \"tokens_used\",\n \"properties\": {\n \"amount\": 1\n },\n \"featureSlug\": \"tokens\",\n \"id\": \"evt_123\",\n \"customerId\": \"cus_123\",\n \"timestamp\": 1741454800000\n}"
response = http.request(request)
puts response.read_body{
"allowed": true,
"state": "processed",
"idempotencyStatus": "already_reported",
"rejectionReason": "LIMIT_EXCEEDED",
"message": "Limit exceeded for meter meter_123"
}{
"error": {
"code": "BAD_REQUEST",
"docs": "https://docs.unprice.dev/api-reference/errors/code/BAD_REQUEST",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "UNAUTHORIZED",
"docs": "https://docs.unprice.dev/api-reference/errors/code/UNAUTHORIZED",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "FORBIDDEN",
"docs": "https://docs.unprice.dev/api-reference/errors/code/FORBIDDEN",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "NOT_FOUND",
"docs": "https://docs.unprice.dev/api-reference/errors/code/NOT_FOUND",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "CONFLICT",
"docs": "https://docs.unprice.dev/api-reference/errors/code/CONFLICT",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "PRECONDITION_FAILED",
"docs": "https://docs.unprice.dev/api-reference/errors/code/PRECONDITION_FAILED",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "PAYLOAD_TOO_LARGE",
"docs": "https://docs.unprice.dev/api-reference/errors/code/PAYLOAD_TOO_LARGE",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "TOO_MANY_REQUESTS",
"docs": "https://docs.unprice.dev/api-reference/errors/code/TOO_MANY_REQUESTS",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}{
"error": {
"code": "INTERNAL_SERVER_ERROR",
"docs": "https://docs.unprice.dev/api-reference/errors/code/INTERNAL_SERVER_ERROR",
"message": "<string>",
"requestId": "req_1234",
"details": {
"kind": "invalid_config",
"issues": [
{
"path": "config.plans[0].version.features[1].featureSlug",
"message": "<string>"
}
]
}
}
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
The synchronous raw event ingestion payload
Logical idempotency key for deduplicating raw events
"idem_123"
The event slug
"tokens_used"
Arbitrary event properties
Show child attributes
Show child attributes
{ "amount": 1 }
The feature slug to verify and ingest synchronously
"tokens"
Optional event id. If omitted, the API will generate an internal event id for processing.
"evt_123"
The unprice customer id
"cus_123"
Event timestamp in epoch milliseconds, if not provided will use the time of the request
1741454800000
Response
The synchronous ingestion result for the targeted feature
Whether the event was accepted and synchronously ingested for the feature
true
Synchronous ingestion lifecycle state for the targeted feature
processed, rejected "processed"
Whether this request applied a new idempotency key or replayed a previously reported event
new, already_reported "already_reported"
Business rejection reason when the event could not be ingested
CUSTOMER_NOT_FOUND, EVENT_TOO_OLD, INVALID_ENTITLEMENT_CONFIGURATION, INVALID_AGGREGATION_PROPERTIES, LIMIT_EXCEEDED, LATE_EVENT_CLOSED_PERIOD, NO_MATCHING_ENTITLEMENT, RUN_BUDGET_EXCEEDED, UNROUTABLE_EVENT, WALLET_EMPTY "LIMIT_EXCEEDED"
Optional details about the synchronous ingestion result
"Limit exceeded for meter meter_123"