curl --request POST \
--url https://api.unprice.dev/v1/budgets/lease \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "customer:acme",
"leaseKey": "lease_01J9Z3",
"amount": "0.05",
"ttlMs": 60000
}
'import requests
url = "https://api.unprice.dev/v1/budgets/lease"
payload = {
"key": "customer:acme",
"leaseKey": "lease_01J9Z3",
"amount": "0.05",
"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', leaseKey: 'lease_01J9Z3', amount: '0.05', ttlMs: 60000})
};
fetch('https://api.unprice.dev/v1/budgets/lease', 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/lease",
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',
'leaseKey' => 'lease_01J9Z3',
'amount' => '0.05',
'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/lease"
payload := strings.NewReader("{\n \"key\": \"customer:acme\",\n \"leaseKey\": \"lease_01J9Z3\",\n \"amount\": \"0.05\",\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/lease")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"customer:acme\",\n \"leaseKey\": \"lease_01J9Z3\",\n \"amount\": \"0.05\",\n \"ttlMs\": 60000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unprice.dev/v1/budgets/lease")
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 \"leaseKey\": \"lease_01J9Z3\",\n \"amount\": \"0.05\",\n \"ttlMs\": 60000\n}"
response = http.request(request)
puts response.read_body{
"lease": {
"leaseKey": "<string>",
"seq": 1,
"granted": "<string>",
"captured": "<string>",
"expiresAt": 123,
"status": "open"
},
"terms": {
"holds": {
"defaultTtlMs": 123,
"maxTtlMs": 123
},
"closesAt": 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>"
}
]
}
}
}debit capacity for a long-lived process to spend locally
Debit capacity against every hard limit before it is spent, for a long-lived process to authorize, settle, and charge in its own memory. Near a limit less than asked for is granted; when nothing fits, 402. Report usage to /v1/budgets/report before expiresAt, or the whole grant counts as used. A retry with the same lease key, amount, and ttlMs returns the same lease. A budget past its closesAt returns 409.
curl --request POST \
--url https://api.unprice.dev/v1/budgets/lease \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "customer:acme",
"leaseKey": "lease_01J9Z3",
"amount": "0.05",
"ttlMs": 60000
}
'import requests
url = "https://api.unprice.dev/v1/budgets/lease"
payload = {
"key": "customer:acme",
"leaseKey": "lease_01J9Z3",
"amount": "0.05",
"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', leaseKey: 'lease_01J9Z3', amount: '0.05', ttlMs: 60000})
};
fetch('https://api.unprice.dev/v1/budgets/lease', 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/lease",
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',
'leaseKey' => 'lease_01J9Z3',
'amount' => '0.05',
'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/lease"
payload := strings.NewReader("{\n \"key\": \"customer:acme\",\n \"leaseKey\": \"lease_01J9Z3\",\n \"amount\": \"0.05\",\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/lease")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"customer:acme\",\n \"leaseKey\": \"lease_01J9Z3\",\n \"amount\": \"0.05\",\n \"ttlMs\": 60000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unprice.dev/v1/budgets/lease")
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 \"leaseKey\": \"lease_01J9Z3\",\n \"amount\": \"0.05\",\n \"ttlMs\": 60000\n}"
response = http.request(request)
puts response.read_body{
"lease": {
"leaseKey": "<string>",
"seq": 1,
"granted": "<string>",
"captured": "<string>",
"expiresAt": 123,
"status": "open"
},
"terms": {
"holds": {
"defaultTtlMs": 123,
"maxTtlMs": 123
},
"closesAt": 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 lease 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"
Up to 256 characters, one per lease, chosen by the client. Retrying a grant with the same key, amount, and ttlMs replays it; another amount or ttlMs returns 409. Lease keys and idempotency keys are separate.
1 - 256"lease_01J9Z3"
Capacity to debit now against every hard limit and spend locally. Near a limit less is granted; when nothing fits, 402.
^\d{1,19}(\.\d{1,8})?$"0.05"
How long the lease lives without a report, in milliseconds: at least 1000 and at most the budget's holds.maxTtlMs. A report renews it once less than half remains. A lease still open at expiry counts its whole grant as used until the window resets.
x > 060000
Response
The lease
Show child attributes
Show child attributes
The budget's hold TTL bounds and close time, so holds spent locally follow them
Show child attributes
Show child attributes
True when this answer replays an earlier request instead of applying it again
Show child attributes
Show child attributes