curl --request POST \
--url https://api.unprice.dev/v1/budgets/authorize \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "customer:acme",
"amount": "0.05",
"idempotencyKey": "run_8f2c:step_3",
"ttlMs": 60000
}
'import requests
url = "https://api.unprice.dev/v1/budgets/authorize"
payload = {
"key": "customer:acme",
"amount": "0.05",
"idempotencyKey": "run_8f2c:step_3",
"ttlMs": 60000
}
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({
key: 'customer:acme',
amount: '0.05',
idempotencyKey: 'run_8f2c:step_3',
ttlMs: 60000
})
};
fetch('https://api.unprice.dev/v1/budgets/authorize', 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/budgets/authorize",
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([
'key' => 'customer:acme',
'amount' => '0.05',
'idempotencyKey' => 'run_8f2c:step_3',
'ttlMs' => 60000
]),
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/budgets/authorize"
payload := strings.NewReader("{\n \"key\": \"customer:acme\",\n \"amount\": \"0.05\",\n \"idempotencyKey\": \"run_8f2c:step_3\",\n \"ttlMs\": 60000\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/budgets/authorize")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"customer:acme\",\n \"amount\": \"0.05\",\n \"idempotencyKey\": \"run_8f2c:step_3\",\n \"ttlMs\": 60000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unprice.dev/v1/budgets/authorize")
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 \"key\": \"customer:acme\",\n \"amount\": \"0.05\",\n \"idempotencyKey\": \"run_8f2c:step_3\",\n \"ttlMs\": 60000\n}"
response = http.request(request)
puts response.read_body{
"allowed": true,
"holdId": "h_42",
"amount": "<string>",
"expiresAt": 123,
"replayed": true,
"balance": {
"remaining": "<string>",
"held": "<string>",
"limits": [
{
"id": "<string>",
"window": "month",
"enforce": "hard",
"amount": "<string>",
"consumed": "<string>",
"held": "<string>",
"remaining": "<string>",
"resetsAt": 123
}
],
"asOf": 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": "BUDGET_EXCEEDED",
"docs": "https://docs.unprice.dev/api-reference/errors/code/BUDGET_EXCEEDED",
"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>"
}
]
}
}
}reserve the maximum permitted usage before work runs
Reserve the maximum permitted usage against every hard limit of the budget, or return 402 with the limit that blocked it. The caller must enforce this maximum during work. Settle final usage up to the reserved amount, or release only after confirming zero usage and no continuing work. A hold still open at its expiresAt is settled at its full amount, never released. A retry with the same idempotency key and request returns the same hold; the same key with a different amount or ttlMs returns 409. A new authorization on a budget past its closesAt returns 409.
curl --request POST \
--url https://api.unprice.dev/v1/budgets/authorize \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "customer:acme",
"amount": "0.05",
"idempotencyKey": "run_8f2c:step_3",
"ttlMs": 60000
}
'import requests
url = "https://api.unprice.dev/v1/budgets/authorize"
payload = {
"key": "customer:acme",
"amount": "0.05",
"idempotencyKey": "run_8f2c:step_3",
"ttlMs": 60000
}
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({
key: 'customer:acme',
amount: '0.05',
idempotencyKey: 'run_8f2c:step_3',
ttlMs: 60000
})
};
fetch('https://api.unprice.dev/v1/budgets/authorize', 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/budgets/authorize",
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([
'key' => 'customer:acme',
'amount' => '0.05',
'idempotencyKey' => 'run_8f2c:step_3',
'ttlMs' => 60000
]),
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/budgets/authorize"
payload := strings.NewReader("{\n \"key\": \"customer:acme\",\n \"amount\": \"0.05\",\n \"idempotencyKey\": \"run_8f2c:step_3\",\n \"ttlMs\": 60000\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/budgets/authorize")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"customer:acme\",\n \"amount\": \"0.05\",\n \"idempotencyKey\": \"run_8f2c:step_3\",\n \"ttlMs\": 60000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unprice.dev/v1/budgets/authorize")
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 \"key\": \"customer:acme\",\n \"amount\": \"0.05\",\n \"idempotencyKey\": \"run_8f2c:step_3\",\n \"ttlMs\": 60000\n}"
response = http.request(request)
puts response.read_body{
"allowed": true,
"holdId": "h_42",
"amount": "<string>",
"expiresAt": 123,
"replayed": true,
"balance": {
"remaining": "<string>",
"held": "<string>",
"limits": [
{
"id": "<string>",
"window": "month",
"enforce": "hard",
"amount": "<string>",
"consumed": "<string>",
"held": "<string>",
"remaining": "<string>",
"resetsAt": 123
}
],
"asOf": 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": "BUDGET_EXCEEDED",
"docs": "https://docs.unprice.dev/api-reference/errors/code/BUDGET_EXCEEDED",
"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 authorize request
The budget's key, chosen by you and unique within the project. Up to 128 letters, digits, and : / _ . @ -, starting with a letter or digit.
^[A-Za-z0-9][A-Za-z0-9:/_.@-]{0,127}$"customer:acme"
The maximum permitted usage to reserve against every hard limit. Settle final usage up to this amount, or release after confirming zero usage and no continuing work.
^\d{1,19}(\.\d{1,8})?$"0.05"
Up to 256 characters. A retry with the same key and the same request replays the first answer; the same key with a different request returns 409.
1 - 256"run_8f2c:step_3"
The execution deadline, in milliseconds. Defaults to the budget's holds.defaultTtlMs and must not exceed holds.maxTtlMs. A hold still open at expiry is settled at its full amount, never released: the capacity stays used until its window resets, and a later settle succeeds only for that amount.
x > 060000
Response
The hold
true The hold, as returned by authorize
^h_[1-9][0-9]{0,15}$"h_42"
The amount held
The deadline for work, in epoch milliseconds. A hold still open then is settled at its full amount.
True when this answer replays an earlier request instead of applying it again
Show child attributes
Show child attributes