Meters API
Configure usage meters on proxies to track consumption, enforce rate limits, or aggregate telemetry without restricting access
Meters API
Configure usage meters on proxies to track and enforce consumption limits across time windows. Meters can count requests or extract numeric values from responses (such as AI token usage), and can optionally apply only to requests matching specific predicates.
Meters can be configured with or without limits. When limits are set, requests that exceed a window threshold are rejected with a 429 response. When no limits are configured, the meter still participates in telemetry aggregation — useful for observability and reporting without restricting API access.
Endpoints
Meters are scoped to a proxy:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/clients/{clientId}/proxies/{proxyId}/meters | List all meters for a proxy |
| GET | /api/clients/{clientId}/proxies/{proxyId}/meters/{meterId} | Get a single meter |
| POST | /api/clients/{clientId}/proxies/{proxyId}/meters | Create a meter |
| PUT | /api/clients/{clientId}/proxies/{proxyId}/meters/{meterId} | Update a meter |
| DELETE | /api/clients/{clientId}/proxies/{proxyId}/meters/{meterId} | Delete a meter |
Meters are proxy-scoped and inherit their region from the parent proxy. The meterRegion field is set automatically at creation time.
Meter Types
| Type | Description |
|---|---|
request_count | Increments a counter by 1 for each matching request |
response_value | Extracts a numeric value from the response (body or header) and accumulates it |
When meterType is response_value, an extraction configuration is required specifying where and how to extract the numeric value from each response.
Create Meter
Create a new usage meter on a proxy.
Request
POST /api/clients/{clientId}/proxies/{proxyId}/meters HTTP/1.1
Host: api.requestrocket.com
Authorization: {user_token}
Content-Type: application/json
{
"meterType": "response_value",
"meterActive": true,
"extraction": {
"location": "body",
"path": "usage.total_tokens",
"defaultValue": 0
},
"limits": {
"day": 100000,
"month": 2000000
},
"notes": "Track AI token usage per day and month"
}Request Body
| Field | Type | Required | Description |
|---|---|---|---|
meterType | string | Yes | request_count or response_value |
meterActive | boolean | Yes | Whether the meter is actively enforced |
limits | object | No | Limit thresholds per time window (see Limits). Omit entirely for aggregate-only meters |
notes | string | No | Description of the meter |
extraction | object | Conditional | Required when meterType is response_value (see Extraction) |
meterMode | string | No | all (default) or conditional |
effect | string | No | For conditional meters: include or exclude |
methods | array | No | HTTP methods this meter applies to |
path | object | No | Path predicate |
query | array | No | Query parameter predicates |
headers | array | No | Header predicates |
body | array | No | Request body predicates |
token | array | No | JWT claim predicates |
variables | array | No | Variable bindings for pattern interpolation |
Limits
The limits object defines the maximum accumulated value allowed per time window. All fields are optional — include only the windows you want to enforce. If limits is omitted entirely, the meter tracks usage in telemetry without ever blocking a request.
| Field | Type | Description |
|---|---|---|
minute | number | Maximum value per minute |
hour | number | Maximum value per hour |
day | number | Maximum value per day |
month | number | Maximum value per month |
Extraction
Required when meterType is response_value. Defines how to extract the numeric value from each response.
| Field | Type | Required | Description |
|---|---|---|---|
location | string | Yes | body or header |
path | string | Conditional | Dot-notation path into the response body (e.g. usage.total_tokens). Required when location is body |
headerName | string | Conditional | Response header name to read. Required when location is header |
defaultValue | number | No | Fallback value used when the extracted value is absent or non-numeric |
Response
{
"meters": [
{
"pKey": "proxy_abc123",
"sKey": "meter_def456",
"userId": "user_789",
"clientId": "client_123",
"parentId": "proxy_abc123",
"meterRegion": "us-east-1",
"meterType": "response_value",
"meterActive": true,
"extraction": {
"location": "body",
"path": "usage.total_tokens",
"defaultValue": 0
},
"limits": {
"day": 100000,
"month": 2000000
},
"notes": "Track AI token usage per day and month",
"meterMode": "all",
"createdAt": "2024-06-01T10:00:00.000Z",
"updatedAt": "2024-06-01T10:00:00.000Z"
}
],
"message": "Success"
}Example
curl -X POST "https://api.requestrocket.com/api/clients/${CLIENT_ID}/proxies/${PROXY_ID}/meters" \
-H "Authorization: ${USER_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"meterType": "response_value",
"meterActive": true,
"extraction": {
"location": "body",
"path": "usage.total_tokens",
"defaultValue": 0
},
"limits": {
"day": 100000,
"month": 2000000
},
"notes": "Track AI token usage per day and month"
}'const response = await fetch(
`https://api.requestrocket.com/api/clients/${clientId}/proxies/${proxyId}/meters`,
{
method: 'POST',
headers: {
'Authorization': process.env.USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
meterType: 'response_value',
meterActive: true,
extraction: {
location: 'body',
path: 'usage.total_tokens',
defaultValue: 0
},
limits: {
day: 100000,
month: 2000000
},
notes: 'Track AI token usage per day and month'
})
}
);
const data = await response.json();
console.log('Meter created:', data.meters[0]);import requests
import os
client_id = "your-client-id"
proxy_id = "your-proxy-id"
response = requests.post(
f'https://api.requestrocket.com/api/clients/{client_id}/proxies/{proxy_id}/meters',
headers={'Authorization': os.getenv('USER_TOKEN')},
json={
'meterType': 'response_value',
'meterActive': True,
'extraction': {
'location': 'body',
'path': 'usage.total_tokens',
'defaultValue': 0
},
'limits': {
'day': 100000,
'month': 2000000
},
'notes': 'Track AI token usage per day and month'
}
)
data = response.json()
print(f"Meter created: {data['meters'][0]['sKey']}")package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type MeterExtraction struct {
Location string `json:"location"`
Path string `json:"path"`
DefaultValue float64 `json:"defaultValue"`
}
type MeterLimits struct {
Day int `json:"day"`
Month int `json:"month"`
}
type MeterRequest struct {
MeterType string `json:"meterType"`
MeterActive bool `json:"meterActive"`
Extraction MeterExtraction `json:"extraction"`
Limits MeterLimits `json:"limits"`
Notes string `json:"notes"`
}
func main() {
clientId := "your-client-id"
proxyId := "your-proxy-id"
requestBody, _ := json.Marshal(MeterRequest{
MeterType: "response_value",
MeterActive: true,
Extraction: MeterExtraction{
Location: "body",
Path: "usage.total_tokens",
DefaultValue: 0,
},
Limits: MeterLimits{Day: 100000, Month: 2000000},
Notes: "Track AI token usage per day and month",
})
url := fmt.Sprintf("https://api.requestrocket.com/api/clients/%s/proxies/%s/meters", clientId, proxyId)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
req.Header.Set("Authorization", os.Getenv("USER_TOKEN"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}import java.net.http.*;
import java.net.URI;
public class CreateMeter {
public static void main(String[] args) throws Exception {
String clientId = "your-client-id";
String proxyId = "your-proxy-id";
String json = "{"
+ "\"meterType\": \"response_value\","
+ "\"meterActive\": true,"
+ "\"extraction\": {"
+ " \"location\": \"body\","
+ " \"path\": \"usage.total_tokens\","
+ " \"defaultValue\": 0"
+ "},"
+ "\"limits\": {\"day\": 100000, \"month\": 2000000},"
+ "\"notes\": \"Track AI token usage per day and month\""
+ "}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.requestrocket.com/api/clients/" + clientId + "/proxies/" + proxyId + "/meters"))
.header("Authorization", System.getenv("USER_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}List Meters
Get all meters configured for a proxy.
Request
GET /api/clients/{clientId}/proxies/{proxyId}/meters HTTP/1.1
Host: api.requestrocket.com
Authorization: {user_token}Response
{
"meters": [
{
"pKey": "proxy_abc123",
"sKey": "meter_def456",
"userId": "user_789",
"clientId": "client_123",
"parentId": "proxy_abc123",
"meterRegion": "us-east-1",
"meterType": "response_value",
"meterActive": true,
"extraction": {
"location": "body",
"path": "usage.total_tokens",
"defaultValue": 0
},
"limits": {
"day": 100000,
"month": 2000000
},
"notes": "Track AI token usage per day and month",
"meterMode": "all",
"createdAt": "2024-06-01T10:00:00.000Z",
"updatedAt": "2024-06-01T10:00:00.000Z"
}
],
"message": "Success"
}Example
curl -X GET "https://api.requestrocket.com/api/clients/${CLIENT_ID}/proxies/${PROXY_ID}/meters" \
-H "Authorization: ${USER_TOKEN}"const response = await fetch(
`https://api.requestrocket.com/api/clients/${clientId}/proxies/${proxyId}/meters`,
{
headers: {
'Authorization': process.env.USER_TOKEN
}
}
);
const data = await response.json();
console.log('Meters:', data.meters);import requests
import os
client_id = "your-client-id"
proxy_id = "your-proxy-id"
response = requests.get(
f'https://api.requestrocket.com/api/clients/{client_id}/proxies/{proxy_id}/meters',
headers={'Authorization': os.getenv('USER_TOKEN')}
)
data = response.json()
print(f"Found {len(data['meters'])} meters")package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type Response struct {
Meters []map[string]interface{} `json:"meters"`
Message string `json:"message"`
}
func main() {
clientId := "your-client-id"
proxyId := "your-proxy-id"
url := fmt.Sprintf("https://api.requestrocket.com/api/clients/%s/proxies/%s/meters", clientId, proxyId)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", os.Getenv("USER_TOKEN"))
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result Response
json.Unmarshal(body, &result)
fmt.Printf("Found %d meters\n", len(result.Meters))
}import java.net.http.*;
import java.net.URI;
import com.google.gson.*;
public class ListMeters {
public static void main(String[] args) throws Exception {
String clientId = "your-client-id";
String proxyId = "your-proxy-id";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.requestrocket.com/api/clients/" + clientId + "/proxies/" + proxyId + "/meters"))
.header("Authorization", System.getenv("USER_TOKEN"))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
JsonArray meters = json.getAsJsonArray("meters");
System.out.println("Found " + meters.size() + " meters");
}
}Get Meter
Retrieve a single meter by ID.
Request
GET /api/clients/{clientId}/proxies/{proxyId}/meters/{meterId} HTTP/1.1
Host: api.requestrocket.com
Authorization: {user_token}Response
{
"meters": [
{
"pKey": "proxy_abc123",
"sKey": "meter_def456",
"userId": "user_789",
"clientId": "client_123",
"parentId": "proxy_abc123",
"meterRegion": "us-east-1",
"meterType": "request_count",
"meterActive": true,
"limits": {
"minute": 60,
"hour": 1000
},
"notes": "Rate limit per credential",
"meterMode": "all",
"createdAt": "2024-06-01T10:00:00.000Z",
"updatedAt": "2024-06-01T10:00:00.000Z"
}
],
"message": "Success"
}Update Meter
Partially update an existing meter. Only the fields you provide are changed.
Request
PUT /api/clients/{clientId}/proxies/{proxyId}/meters/{meterId} HTTP/1.1
Host: api.requestrocket.com
Authorization: {user_token}
Content-Type: application/json
{
"meterActive": false,
"limits": {
"day": 50000,
"month": 1000000
}
}Request Body
All fields are optional. Fields omitted from the request retain their current values.
| Field | Type | Description |
|---|---|---|
meterType | string | request_count or response_value |
meterActive | boolean | Whether the meter is actively enforced |
limits | object | Limit thresholds per time window |
notes | string | Description of the meter |
extraction | object | Extraction configuration |
meterMode | string | all or conditional |
effect | string | include or exclude |
methods | array | HTTP methods this meter applies to |
path | object | Path predicate |
query | array | Query parameter predicates |
headers | array | Header predicates |
body | array | Request body predicates |
token | array | JWT claim predicates |
variables | array | Variable bindings for pattern interpolation |
Response
{
"meters": [
{
"pKey": "proxy_abc123",
"sKey": "meter_def456",
"userId": "user_789",
"clientId": "client_123",
"parentId": "proxy_abc123",
"meterRegion": "us-east-1",
"meterType": "response_value",
"meterActive": false,
"extraction": {
"location": "body",
"path": "usage.total_tokens",
"defaultValue": 0
},
"limits": {
"day": 50000,
"month": 1000000
},
"notes": "Track AI token usage per day and month",
"meterMode": "all",
"createdAt": "2024-06-01T10:00:00.000Z",
"updatedAt": "2024-06-02T08:30:00.000Z"
}
],
"message": "Success"
}Example
curl -X PUT "https://api.requestrocket.com/api/clients/${CLIENT_ID}/proxies/${PROXY_ID}/meters/${METER_ID}" \
-H "Authorization: ${USER_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"meterActive": false,
"limits": {
"day": 50000,
"month": 1000000
}
}'const response = await fetch(
`https://api.requestrocket.com/api/clients/${clientId}/proxies/${proxyId}/meters/${meterId}`,
{
method: 'PUT',
headers: {
'Authorization': process.env.USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
meterActive: false,
limits: {
day: 50000,
month: 1000000
}
})
}
);
const data = await response.json();
console.log('Meter updated:', data.meters[0]);import requests
import os
client_id = "your-client-id"
proxy_id = "your-proxy-id"
meter_id = "your-meter-id"
response = requests.put(
f'https://api.requestrocket.com/api/clients/{client_id}/proxies/{proxy_id}/meters/{meter_id}',
headers={'Authorization': os.getenv('USER_TOKEN')},
json={
'meterActive': False,
'limits': {
'day': 50000,
'month': 1000000
}
}
)
data = response.json()
print(f"Meter updated: {data['meters'][0]['sKey']}")package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type MeterUpdate struct {
MeterActive bool `json:"meterActive"`
Limits MeterLimits `json:"limits"`
}
type MeterLimits struct {
Day int `json:"day"`
Month int `json:"month"`
}
func main() {
clientId := "your-client-id"
proxyId := "your-proxy-id"
meterId := "your-meter-id"
requestBody, _ := json.Marshal(MeterUpdate{
MeterActive: false,
Limits: MeterLimits{Day: 50000, Month: 1000000},
})
url := fmt.Sprintf("https://api.requestrocket.com/api/clients/%s/proxies/%s/meters/%s", clientId, proxyId, meterId)
req, _ := http.NewRequest("PUT", url, bytes.NewBuffer(requestBody))
req.Header.Set("Authorization", os.Getenv("USER_TOKEN"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}import java.net.http.*;
import java.net.URI;
public class UpdateMeter {
public static void main(String[] args) throws Exception {
String clientId = "your-client-id";
String proxyId = "your-proxy-id";
String meterId = "your-meter-id";
String json = "{"
+ "\"meterActive\": false,"
+ "\"limits\": {\"day\": 50000, \"month\": 1000000}"
+ "}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.requestrocket.com/api/clients/" + clientId + "/proxies/" + proxyId + "/meters/" + meterId))
.header("Authorization", System.getenv("USER_TOKEN"))
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}Delete Meter
Remove a meter from a proxy.
Request
DELETE /api/clients/{clientId}/proxies/{proxyId}/meters/{meterId} HTTP/1.1
Host: api.requestrocket.com
Authorization: {user_token}Response
{
"message": "Meter deleted successfully from all tables"
}Example
curl -X DELETE "https://api.requestrocket.com/api/clients/${CLIENT_ID}/proxies/${PROXY_ID}/meters/${METER_ID}" \
-H "Authorization: ${USER_TOKEN}"const response = await fetch(
`https://api.requestrocket.com/api/clients/${clientId}/proxies/${proxyId}/meters/${meterId}`,
{
method: 'DELETE',
headers: {
'Authorization': process.env.USER_TOKEN
}
}
);
const data = await response.json();
console.log(data.message);import requests
import os
client_id = "your-client-id"
proxy_id = "your-proxy-id"
meter_id = "your-meter-id"
response = requests.delete(
f'https://api.requestrocket.com/api/clients/{client_id}/proxies/{proxy_id}/meters/{meter_id}',
headers={'Authorization': os.getenv('USER_TOKEN')}
)
data = response.json()
print(data['message'])package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type Response struct {
Message string `json:"message"`
}
func main() {
clientId := "your-client-id"
proxyId := "your-proxy-id"
meterId := "your-meter-id"
url := fmt.Sprintf("https://api.requestrocket.com/api/clients/%s/proxies/%s/meters/%s", clientId, proxyId, meterId)
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Set("Authorization", os.Getenv("USER_TOKEN"))
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result Response
json.Unmarshal(body, &result)
fmt.Println(result.Message)
}import java.net.http.*;
import java.net.URI;
import com.google.gson.*;
public class DeleteMeter {
public static void main(String[] args) throws Exception {
String clientId = "your-client-id";
String proxyId = "your-proxy-id";
String meterId = "your-meter-id";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.requestrocket.com/api/clients/" + clientId + "/proxies/" + proxyId + "/meters/" + meterId))
.header("Authorization", System.getenv("USER_TOKEN"))
.DELETE()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
System.out.println(json.get("message").getAsString());
}
}Conditional Metering
By default a meter applies to all requests (meterMode: "all"). Set meterMode to "conditional" to restrict metering to requests that match (or do not match) a set of predicates.
| Field | Type | Description |
|---|---|---|
meterMode | string | all — meter every request; conditional — filter by predicates |
effect | string | include — only meter matching requests; exclude — meter all requests except matching ones |
methods | array | HTTP methods to match (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) |
path | object | Path predicate |
query | array | Query parameter predicates (AND semantics) |
headers | array | Header predicates (AND semantics) |
body | array | Request body predicates (AND semantics) |
token | array | JWT claim predicates (AND semantics) |
variables | array | Variable bindings for pattern interpolation |
Example — Track Token Usage Only for POST Requests
{
"meterType": "response_value",
"meterActive": true,
"meterMode": "conditional",
"effect": "include",
"methods": ["POST"],
"extraction": {
"location": "body",
"path": "usage.total_tokens",
"defaultValue": 0
},
"limits": {
"day": 100000
},
"notes": "Count tokens only on POST completions"
}Example — Exclude Health Check Requests from Count
{
"meterType": "request_count",
"meterActive": true,
"meterMode": "conditional",
"effect": "exclude",
"path": {
"pattern": "^/health",
"presence": "present"
},
"limits": {
"minute": 60,
"hour": 1000
},
"notes": "Rate limit all requests except health checks"
}Meter Object Reference
The full meter object returned by GET, POST, and PUT responses:
| Field | Type | Description |
|---|---|---|
pKey | string | Parent key (proxy ID) |
sKey | string | Sort key (meter ID) |
userId | string | ID of the user who created the meter |
clientId | string | ID of the client that owns the meter |
parentId | string | Parent proxy ID |
meterRegion | string | AWS region inherited from the parent proxy |
meterType | string | request_count or response_value |
meterActive | boolean | Whether the meter is actively enforced |
limits | object | Limit thresholds per time window |
extraction | object | Extraction config (present when meterType is response_value) |
notes | string | Description of the meter |
meterMode | string | all or conditional |
effect | string | include or exclude (for conditional meters) |
methods | array | HTTP methods this meter applies to |
path | object | Path predicate |
query | array | Query parameter predicates |
headers | array | Header predicates |
body | array | Request body predicates |
token | array | JWT claim predicates |
variables | array | Variable bindings |
createdAt | string | ISO 8601 creation timestamp |
updatedAt | string | ISO 8601 last-updated timestamp |