Ana içeriğe geç

Limit Plans Management API

Overview​

A limit plan is a reusable catalog entry that is applied to identities. A plan holds its own rule body and an active flag; it keeps no revisions and no drafts. A saved rule body applies at once: when the plan is active, every assignment that references it is re-pushed to the gateways. A plan is either active (the gateways enforce it) or inactive (it limits nothing, while its rule body, assignments and counters are kept); the active field of the plan response says which. This page documents the Management API surface for that catalog, the portable counterpart of the manager's own Limit Plans REST endpoints. For the concepts behind it — what each family carries and how plans are assigned — see Limit Plans.

Revisions removed

Earlier versions kept a revision ladder under each plan (draft, publish, deploy). Plans no longer keep revisions: the read endpoints GET .../revisions, GET .../revisions/{revision}, GET .../draft and GET .../published are removed and answer 404. The revision-era write endpoints remain as deprecated aliases — see Deprecated aliases.

Every address on this page is under /apiops/projects/{projectName} and follows the shared Common Contract: the stable error envelope, correlation IDs, bounded pagination, and Idempotency-Key on plan creation. A caller outside the project's scope always gets 404 resourceNotFound, never 403.

References are portable — no Mongo id ever crosses the boundary. A plan is always addressed by its planKey, minted by the server and returned in the response.

Platform scope (projectName=admin)​

projectName names the plan's owning scope. The special admin project — reachable only by a system administrator — addresses the installation-wide platform catalog instead of one project's own: the plans every project can bind to but only the platform can edit.

Reads and writes by planKey (GET/PUT/DELETE .../{planKey}, rule-spec, activate, deactivate) are scoped strictly to the calling project — a project-scoped call can never resolve a platform-only planKey this way, even though GET /limit-plans?includePlatform=true surfaces it in a list. Reaching a platform plan by key requires calling through the admin project.

GET /limit-plans?includePlatform=true adds the platform's plans to a project's own listing; they come back with platformScoped: true, read-only from that project.

Profiles owned by a subscription plan​

A portal subscription plan that carries a rate limit or a quota owns a generated REQUEST_RATE profile, republished by the platform whenever that plan's numbers change. Such a plan is omitted from the listing by default and every write on it is refused — see managedBySubscriptionPlan under Errors. Pass includeManaged=true on GET /limit-plans to list them; each one comes back with managedBySubscriptionPlanId and managedBySubscriptionPlanName populated, and the limits are changed through the owning subscription plan rather than here.

Endpoints​

Plans​

MethodPathDescription
GET/limit-plansList the scope's plans, sortable by name, family, or active
POST/limit-plansCreate a plan with its rule body, active (201 + Location)
GET/limit-plans/{planKey}One plan: its metadata, its active flag and its rule body
PUT/limit-plans/{planKey}Update a plan's name, description, or labels (not its rule body)
DELETE/limit-plans/{planKey}Delete a plan that no assignment and no creation default references (204)

Rule body and status​

MethodPathDescription
PUT/limit-plans/{planKey}/rule-specReplace the plan's rule body; it applies at once
POST/limit-plans/{planKey}/actions/activateThe gateways enforce the plan again for every assignment that references it
POST/limit-plans/{planKey}/actions/deactivateStop enforcing the plan on every gateway; the rule body, assignments and counters are kept

Deprecated aliases​

These endpoints are kept so that pipelines written for the revision model keep working. New integrations should use the endpoints above.

MethodPathBehavior now
POST/limit-plans/{planKey}/draft-revisionsSaves the rule body like PUT .../rule-spec (it applies at once) and answers 200 with the plan — no longer 201
PUT/limit-plans/{planKey}/draft-revisions/{revision}Saves the rule body like PUT .../rule-spec; the revision number is ignored
POST/limit-plans/{planKey}/revisions/{revision}/actions/publishAnswers the plan as it is; the revision number is ignored
POST/limit-plans/{planKey}/actions/deployAlias of actions/activate
POST/limit-plans/{planKey}/actions/undeployAlias of actions/deactivate
POST/limit-plans/{planKey}/actions/retireRemoved. Always 400 malformedRequest (params.reason = limit.plan.retireRemoved); deactivate the plan instead

List plans​

GET /limit-plans?family&includePlatform&includeManaged&page&size&sort sorts by name,asc by default, tie-broken by planKey; sort may also name family or active (lifecycle is still accepted as a deprecated alias of active). A sort naming any other field is 400 unsupportedSortField. family narrows the listing to one family; includePlatform=true is described under Platform scope above, and includeManaged=true under Profiles owned by a subscription plan. The rows of the listing do not carry ruleSpec; read one plan to get its rule body.

Save the rule body​

PUT /limit-plans/{planKey}/rule-spec replaces the plan's limits. When the plan is active, every assignment that references it is re-pushed at once; when it is inactive, the body is stored and takes effect with the next activate. A body equal to the stored one changes nothing but the plan's last-modified stamp. The response is the plan, its rule body included. See Rule Spec for the body.

Activate​

POST /limit-plans/{planKey}/actions/activate makes the gateways enforce the plan again for every assignment that references it, and re-pushes those assignments. An active plan is left as it is. The response is the plan, with active: true.

Deactivate​

POST /limit-plans/{planKey}/actions/deactivate stops enforcing the plan on every gateway for all of its assignments. The rule body, the assignments and the counters are kept; counters are not reset, so a later activate continues from the current usage. An inactive plan is left as it is. The response is the plan, with active: false.

Delete an unused plan​

DELETE /limit-plans/{planKey} removes a plan in any state, active or inactive, as long as nothing references it. A plan that a limit assignment of any status references (an ended assignment included, since it keeps naming the plan as history), or that a creation default references, is refused with 409 lifecycleConflict (params.reason = limit.plan.inUseByBinding). The manager screen's delete ends the assignments and removes the plan from the creation defaults first; this endpoint does not.

Idempotency​

POST /limit-plans requires an Idempotency-Key header (see Common Contract → Idempotency); a missing or blank header is 400 idempotencyKeyRequired. The key is claimed per scope and per operation, together with a canonical hash of the request body:

SituationResult
New keyThe plan is created
Same key, same body, first call already finished409 idempotencyReplayExpired — params.resource names the plan key the first call created; read it with GET .../{planKey} instead
Same key, same body, first call still in flight409 idempotencyInProgress
Same key, a different body409 idempotencyKeyReused

A claim whose underlying call failed is released, so the same key may be retried once the cause is fixed. A create at platform scope (through the admin project) claims its key under that same scope, separately from any project's own claims.

Every other write on this page — saving the rule body, activating, deactivating, deleting — is naturally idempotent (the same body or status twice changes nothing) and takes no Idempotency-Key.

Request Body​

Create a plan — POST /limit-plans​

FieldTypeRequiredMeaning
familyenumYesREQUEST_RATE or AI_TOKEN_BUDGET — immutable after creation
namestringYesEditable later with PUT
nameTrstringNoTurkish display name; left blank or omitted, the plan's name is copied into it
descriptionstringNo
labelsstring[]NoFree-form tags
initialRuleSpecobjectYes in practiceThe plan's rule body — see Rule Spec below

initialRuleSpec is nullable on the wire, but a plan is always created with its rule body, so a create that omits it is refused with 400 malformedRequest (params.reason = limit.plan.ruleSpecRequired).

The plan is created active, with version: 1. It limits nobody until it is assigned to an identity.

The name is unique per scope and family, not globally: creating a plan whose family + name combination already exists in the target scope is 409 lifecycleConflict (params.reason = limit.plan.nameAlreadyExists). The planKey is minted from the name and checked for a collision before the plan is saved, so its own conflict (params.reason = limit.plan.keyAlreadyExists) is only reachable under an actual race between two concurrent creates for the same name.

Update a plan's metadata — PUT /limit-plans/{planKey}​

Only the plan's descriptive half is editable this way — its family, planKey, and owning scope are immutable, and its rule body is saved with PUT .../rule-spec, not here.

FieldTypeRequiredMeaning
namestringYes
nameTrstringNoTurkish display name; left blank or omitted, the plan's name is copied into it
descriptionstringNo
labelsstring[]NoReplaces the stored list
versionintegerYes in practiceThe version last read from the plan

version is nullable on the wire but required in practice: a plan's version starts at 1 on creation and only increases, while an omitted version defaults to 0 on the wire — so omitting it is always refused as 409 lifecycleConflict (params.reason = limit.plan.staleVersion). Sending an explicit version that no longer matches the stored plan is 409 versionConflict instead (see Common Contract → Optimistic concurrency). Always send the version your last read of the plan returned.

The manager screen no longer collects a separate Turkish name and always sends nameTr equal to name, on both create and update; a caller integrating directly against this API may still set a distinct nameTr.

Rule Spec​

PUT /limit-plans/{planKey}/rule-spec (and the deprecated draft-revisions aliases) take one body:

FieldTypeRequiredMeaning
ruleSpecobjectYesThe plan's rule body — exactly one of the two arms below, matching the plan's family
versionintegerNoThe plan version you last read. When sent and the plan has changed since, the save is refused with 409 lifecycleConflict (params.reason = limit.plan.staleVersion); when omitted, no check is made
changeReasonstringNoAccepted and ignored; plans keep no revision history

The ruleSpec of POST /limit-plans (initialRuleSpec) has the same shape.

A ruleSpec that is missing, that carries both arms, or that carries the arm for the wrong family, is 400 malformedRequest — missing is params.reason = limit.plan.ruleSpecRequired, the wrong/extra arm is params.reason = limit.plan.ruleSpecFamilyMismatch.

Plan settings and limits​

A plan has no general limit: every limit names its own target and sits in ruleSpec.targetedRules. The family arm at the top of the ruleSpec (requestRate for a REQUEST_RATE plan, aiBudget for an AI_TOKEN_BUDGET plan) is required, but it carries only the plan's settings. A ceiling on it — a request-rate window, or a token, USD or request-count ceiling on the budget, including per-model and per-provider budgets — is refused with 400 malformedRequest (params.field names it, for example ruleSpec.requestRate.windows or initialRuleSpec.aiBudget).

Request Rate arm — ruleSpec.requestRate​

FieldTypeRequiredMeaning
overflowActionenumNoBLOCK (default when absent) refuses the request once a window is exhausted; COUNT_ONLY lets it through and keeps counting
cacheErrorHandlingTypeenumNoFAIL rejects the request when the counter service cannot be reached; CONTINUE lets it through uncounted
cacheConnectionTimeoutInSecondsintegerNoTimeout to the counter service, in seconds
showRateLimitStatisticsInResponseHeaderbooleanNoAdds the remaining-limit headers to the response
customMessageTemplate / customMessageContentType / customMessageHttpStatusCodestring / string / integerNoReplaces the standard message when a limit is exceeded

Each element of ruleSpec.targetedRules of a REQUEST_RATE plan is one limit: its target and a requestRate with the windows of that limit.

FieldTypeRequiredMeaning
windowsarrayYes, non-emptyOne ceiling per time window — see below

Each entry of windows:

FieldTypeRequiredMeaning
permittedMessageCountintegerYes, >= 1The ceiling for this window
intervalenumYesONE_SECOND | ONE_MINUTE | ONE_HOUR | ONE_DAY | ONE_MONTH
periodLengthintegerNo, >= 1How many of interval make up one window (e.g. interval=ONE_HOUR, periodLength=6 is a 6-hour window)
windowTypeenumNoFIXED or SLIDING
countingModeenumNoALL_REQUESTS counts a request even when it is the one that gets blocked; ACCEPTED_ONLY counts only requests that reached the backend

No default is filled in on unset optional fields here — they are read back exactly as sent, absent members included. Two windows over the same interval and periodLength are refused.

Targets of a limit — target​

A limit names its target by name, never by an installation id.

FieldTypeMeaning
typeenumREQUEST_RATE plans: API_PROXY, API_PROXY_ENV, API_PROXY_GROUP, API_PROXY_GROUP_ENV, API_ENDPOINT, API_ENDPOINT_ENV. AI_TOKEN_BUDGET plans: API_PROXY, API_PROXY_ENV (an AI proxy), AI_MODEL, AI_PROVIDER. Any other type is 400 malformedRequest (params.reason = limit.plan.targetInvalid)
apiProxyNamestringAPI_PROXY, API_PROXY_ENV, and the endpoint types
apiProxyGroupNamestringAPI_PROXY_GROUP, API_PROXY_GROUP_ENV
environmentNamestringThe _ENV types
endpointHttpMethod / endpointPathstringThe endpoint types: the endpoint's HTTP method and path, as the API proxy defines them
llmProviderNamestringAI_PROVIDER: the LLM provider. AI_MODEL: the provider that owns the model
targetKeystringAI_MODEL: the model identifier, as listed by the provider
projectNamestringadmin project only: the project of the API proxy, group or endpoint
httpMethodsstring[]API_PROXY and API_PROXY_ENV only; empty or absent means every method

A model limit names one model of one LLM provider (llmProviderName plus targetKey) and counts only the traffic that provider's model served; the same model identifier on another provider is not counted. The provider must be one the plan's project can use and must list the model, otherwise the request is 400 malformedRequest (params.reason = limit.plan.targetInvalid). Two limits of one plan with the same target are 400 malformedRequest (params.reason = limit.plan.targetDuplicate).

Other ruleSpec fields​

Next to the family arm, the ruleSpec body also carries:

FieldTypeMeaning
targetedRulesarrayThe plan's limits. Each element repeats the family arm for one API proxy, API proxy group, endpoint, AI proxy, model or provider, and carries its target by name (API proxy, proxy group, endpoint as proxy + HTTP method + path, environment, LLM provider, and for a model the provider plus the model). An unknown name is 400 malformedRequest (params.reason = limit.plan.targetNotFound, params.field names the limit). In the admin project a target also names its projectName. A targeted AI Token Budget limit must set at least one ceiling (params.reason = limit.plan.noLimits).
validFrom / validUntiltimestampThe plan-level validity window; absent on a side means open on that side
conditionobjectThe plan's request condition: the plan counts and blocks only the requests that meet it; absent means every request
errorMessageListarrayThe plan's error message customizations for the errors its limits raise; absent means the default messages
ownBudgetThresholdsbooleanAI Token Budget targeted limit only: true when the limit's alert thresholds and hard cap are its own; absent takes the general budget's values

A rule body save that adds a limit another plan of an identity holding this plan already sets (the same API, endpoint or AI proxy, counted the same way) is 409 lifecycleConflict (params.reason = limit.plan.targetOverlap). An overlap the previous body already had does not block.

AI Token Budget arm — ruleSpec.aiBudget​

The budget object has these fields. On the top-level arm only the plan's settings apply (softCapPercents, hardCapPercent, overflowAction, cheaperModelId, the counter-store and custom message settings); the ceilings below are set on the budget of each targeted limit.

Budget Object Fields​
FieldTypeRequiredDefaultDescription
enabledbooleanNofalseWhether the budget is enabled
tokensPerHourintegerNo-Hourly token cap. null = no limit; if set, must be >= 1
tokensPerMinuteintegerNo-Per-minute token cap. null = no limit; if set, must be >= 1
tokensPerDayintegerNo-Daily token cap. null = no limit; if set, must be >= 1
tokensPerMonthintegerNo-Monthly token cap. null = no limit; if set, must be >= 1
monthlyBudgetUsdnumberNo-Monthly USD cost cap. null = no cost limit; if set, must be > 0
usdPerMinutenumberNo-Per-minute USD cost cap. null = no limit; if set, must be > 0. Not available in modelBudgets / providerBudgets
usdPerHournumberNo-Hourly USD cost cap. null = no limit; if set, must be > 0
usdPerDaynumberNo-Daily USD cost cap. null = no limit; if set, must be > 0
showRateLimitStatisticsInResponseHeaderbooleanNonullWhether the response carries the remaining-limit headers. null = the assignment's setting applies; true / false = the plan decides. The headers come from the Limit Plan tier with the smallest share left, among the token and call-count tiers; USD tiers are never used
requestsPerMinuteintegerNo-Per-minute LLM call count cap. null = no limit; if set, must be >= 1. Not available in modelBudgets / providerBudgets
requestsPerHourintegerNo-Hourly LLM call count cap. Same rules as requestsPerMinute
requestsPerDayintegerNo-Daily LLM call count cap. Same rules as requestsPerMinute
requestsPerMonthintegerNo-Monthly LLM call count cap. Same rules as requestsPerMinute
softCapPercentsarray[integer]No[]Soft-cap warning thresholds. Each element in range [0, 100]. When omitted, [] is stored; a GET returns [] explicitly when no thresholds are set
hardCapPercentintegerNo100Hard-cap percent, range [1, 100]
overflowActionstringNoBLOCKAction on overflow: BLOCK, FAILOVER, ALERT_ONLY or CHEAPER_MODEL
cheaperModelIdstringNo-Cheaper fallback model id (required behaviour when overflowAction=CHEAPER_MODEL)
modelBudgetsarrayNokeepPer-model budget overrides (max 50). See list-semantics note below
providerBudgetsarrayNokeepPer-provider budget overrides (max 50). See list-semantics note below
Model / Provider Budget Entry Fields​
FieldTypeRequiredDescription
modelIdstringYes (model entry)Model id the override applies to
providerRefstringYes (provider entry)LLM provider the override applies to (the provider reference, not its name)
tokensPerHour / tokensPerDay / tokensPerMonthintegerNoToken caps for this model/provider. If set, must be >= 1
monthlyBudgetUsd / usdPerHour / usdPerDaynumberNoUSD cost caps for this model/provider. If set, must be > 0
overflowActionstringNoPer-entry overflow action (defaults to BLOCK)
  • A request-count ceiling counts one call to the LLM provider for each request and applies on the targeted limits of an AI proxy, model or provider. The Token Rate Limit policy does not accept request-count ceilings.
  • An empty array [] in modelBudgets / providerBudgets clears the overrides; null or an absent field keeps the stored ones.
  • A USD limit meters only models that have an input price in the LLM provider. A model without a price counts as 0, so the limit does not meter it and the save is not refused. Set the missing price, or use a token-based limit instead.

Response​

Plan — 200/201​

The shape returned by every plan endpoint that answers with a body; DELETE answers 204 with no body.

{
"planKey": "standard-100rps",
"family": "REQUEST_RATE",
"name": "Standard 100 rps",
"nameTr": "Standard 100 rps",
"description": "Default tier for partner API clients",
"labels": ["partner", "tier-1"],
"active": true,
"deployed": true,
"platformScoped": false,
"ruleSpec": {
"requestRate": { "overflowAction": "BLOCK" },
"targetedRules": [
{
"target": { "type": "API_PROXY", "apiProxyName": "payments-api" },
"requestRate": {
"windows": [
{ "permittedMessageCount": 100, "interval": "ONE_SECOND" }
]
}
}
]
},
"managedBySubscriptionPlanId": null,
"managedBySubscriptionPlanName": null,
"version": 3
}
FieldTypeMeaning
planKeystringOpaque public key, minted at creation
familyenumREQUEST_RATE or AI_TOKEN_BUDGET
name / nameTr / description / labelsstring / string / string / string[]Descriptive metadata
activebooleantrue when the gateways enforce the plan; moved by activate and deactivate
deployedbooleanDeprecated alias of active, kept for callers written before deploy became activate
platformScopedbooleantrue when this plan lives in the platform catalog rather than a project's own
ruleSpecobjectThe plan's rule body, targets named — see Rule Spec. Returned by GET .../{planKey}, PUT .../rule-spec, activate, deactivate and the deprecated aliases; not returned by POST /limit-plans, PUT .../{planKey} or the list rows
managedBySubscriptionPlanIdstringThe portal subscription plan that owns this profile, or null for an ordinary catalog entry. Every write on an owned profile is refused — see Profiles owned by a subscription plan
managedBySubscriptionPlanNamestringThe owning subscription plan's display name, or null
versionintegerThe optimistic-concurrency token for the version field of PUT .../{planKey} and PUT .../rule-spec; every save and every status change increases it

The lifecycle, currentPublishedRevision, retiredAt and retiredBy fields of earlier versions are no longer returned.

List envelope — 200​

GET /limit-plans returns the shared paged envelope (see Common Contract → Pagination):

{
"status": "SUCCESS",
"items": [],
"page": 0,
"size": 20,
"totalElements": 12,
"totalPages": 1,
"sort": ["name,asc"]
}

Errors​

Every error follows the Common Contract's error envelope — branch on errorKey, never on message.

Error keyWhen
malformedRequestThe body could not be understood or was left empty (params.reason = bodyRequired), the plan is missing its family (params.reason = limit.plan.familyRequired) or name (params.reason = limit.plan.nameRequired), a rule spec is missing (params.reason = limit.plan.ruleSpecRequired) or carries the wrong/extra arm for the plan's family (params.reason = limit.plan.ruleSpecFamilyMismatch), a targeted limit names an unknown target (params.reason = limit.plan.targetNotFound), a token or request-count limit is below 1 or a USD limit is 0 or less (params.reason = limit.plan.ceilingNotPositive), a top-level ceiling is sent where only a targeted limit may carry one (params.field names it), a targeted limit has a target its family cannot use or names the same target twice (params.reason = limit.plan.targetInvalid / limit.plan.targetDuplicate), a targeted AI Token Budget limit sets no ceiling (params.reason = limit.plan.noLimits), or the removed retire action was called (params.reason = limit.plan.retireRemoved)
resourceNotFoundAn unknown planKey (params.reason = limit.plan.notFound) — including a project-scoped call naming a platform-only planKey, see Platform scope. The removed revision read endpoints (.../revisions, .../revisions/{revision}, .../draft, .../published) also answer 404
versionConflictPUT .../{planKey}'s version field no longer matches the stored plan
lifecycleConflictparams.reason names the service cause: limit.plan.staleVersion (a stale version on PUT .../rule-spec, or see the version note under Update a plan's metadata above), limit.plan.targetOverlap (a limit that another plan of the same identities already sets), limit.plan.inUseByBinding (deleting a plan that an assignment or a creation default references), limit.plan.nameAlreadyExists / limit.plan.keyAlreadyExists (duplicate within the scope), or limit.plan.keyImmutable / limit.plan.familyImmutable / limit.plan.projectImmutable (an immutable field was sent on update)
idempotencyKeyRequiredPOST /limit-plans was missing its Idempotency-Key header
idempotencyKeyReusedThe same key was sent with a different request body
idempotencyReplayExpiredThe same key and body already completed — params.resource names the plan it created
idempotencyInProgressThe same key and body's first call is still running
permissionDeniedAuthenticated, but the caller lacks Identity/AI Budgets Manage (writes) or View (reads)
unsupportedSortFieldsort on GET /limit-plans named a field other than name, family, active, or the deprecated lifecycle
malformedRequest with params.reason = limit.plan.managedBySubscriptionPlanA write (PUT .../{planKey}, DELETE .../{planKey}, PUT .../rule-spec, activate, deactivate, or a deprecated alias that saves or changes status) targeted a profile a portal subscription plan owns. Change the owning plan's rate limit and quota instead — see Profiles owned by a subscription plan

See Common Contract → Error keys for the complete closed set and the status-code table.

Permissions & Licensing​

  • Reads (GET) require the Identity permission at View; writes (POST/PUT/DELETE) require it at Manage. The AI Budgets permission is also accepted in place of Identity.
  • No license module is checked on this surface: an AI_TOKEN_BUDGET plan is gated by permission exactly like a REQUEST_RATE plan.
  • Writing a platform-scope plan (any endpoint through the admin project) is restricted to a system administrator, the same rule the rest of the manager's admin-scoped endpoints follow.
  • A project outside the caller's authorized scope is always 404 resourceNotFound, never 403 — see Common Contract → Telling 402, 403 and 404 apart.

Example​

Create a plan, raise its ceiling, then deactivate and activate it again:

# 1. Create — requires Idempotency-Key; the plan is created active with its rule body
curl -X POST "https://apinizer.example.com/apiops/projects/finance/limit-plans" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1a9c3e-2d4b-4a7c-9e1f-8b3a1d5e7c40" \
-d '{
"family": "REQUEST_RATE",
"name": "Standard 100 rps",
"description": "Default tier for partner API clients",
"labels": ["partner", "tier-1"],
"initialRuleSpec": {
"requestRate": { "overflowAction": "BLOCK" },
"targetedRules": [
{
"target": { "type": "API_PROXY", "apiProxyName": "payments-api" },
"requestRate": {
"windows": [
{ "permittedMessageCount": 100, "interval": "ONE_SECOND" }
]
}
}
]
}
}'
# 201 Created, Location: /apiops/projects/finance/limit-plans/standard-100rps

# 2. Raise the ceiling — applies at once to every assignment of the active plan
curl -X PUT "https://apinizer.example.com/apiops/projects/finance/limit-plans/standard-100rps/rule-spec" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"version": 1,
"ruleSpec": {
"requestRate": { "overflowAction": "BLOCK" },
"targetedRules": [
{
"target": { "type": "API_PROXY", "apiProxyName": "payments-api" },
"requestRate": {
"windows": [
{ "permittedMessageCount": 150, "interval": "ONE_SECOND" }
]
}
}
]
}
}'
# 200 OK — the plan, "version": 2

# 3. Stop enforcing the plan; its rule body, assignments and counters are kept
curl -X POST "https://apinizer.example.com/apiops/projects/finance/limit-plans/standard-100rps/actions/deactivate" \
-H "Authorization: Bearer $TOKEN"
# 200 OK — the plan, "active": false

# 4. Enforce it again
curl -X POST "https://apinizer.example.com/apiops/projects/finance/limit-plans/standard-100rps/actions/activate" \
-H "Authorization: Bearer $TOKEN"
# 200 OK — the plan, "active": true
  • Limit Plans, Assignments & Defaults API - The manager's own REST surface behind Limit Plans, including plan CRUD, the rule body save, activate and deactivate
  • Bulk Limit Assignment API - Attach a plan to a large, filtered population of subjects in one run
  • Limit Plans - The manager UI screens and concepts this surface backs
  • Common Contract - The shared error envelope, idempotency, pagination, and status-code rules every endpoint on this page follows
  • Error Handling - The envelope used by the manager's older endpoints