Dropto Plans API
The tiers and every ceiling they carry, read from the same table the server enforces against. The pricing page and the dashboard both read this, which is what stops a published limit and an enforced one from drifting apart.
The tiers and every ceiling they carry, read from the same table the server enforces against. The pricing page and the dashboard both read this, which is what stops a published limit and an enforced one from drifting apart.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/dropto-run:dropto-run-plans-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Drop2Run.Api | v1 Plans API
version: 1.0.0
description: The tiers and every ceiling they carry, read from the same table the server enforces against. The pricing page and the dashboard both read this, which is what stops a published limit and an enforced one from drifting apart.
servers:
- description: Production
url: https://dropto.run
tags:
- description: The tiers and every ceiling they carry, read from the same table the server enforces against. The pricing page and the dashboard both read this, which is what stops a published limit and an enforced one from drifting apart.
name: Plans
paths:
/api/plans:
get:
operationId: GetPlans
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PlansResponse'
description: OK
summary: Lists the quota tiers the API enforces
tags:
- Plans
/api/plans/mine:
get:
operationId: GetMyPlans
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PlansResponse'
description: OK
'403':
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
description: Forbidden
security:
- session: []
- accessToken: []
summary: Lists the quota tiers this account may buy, including any agreed with it
tags:
- Plans
components:
schemas:
PlansResponse:
description: Response of `GET /plans`.
properties:
anonymousPlanId:
description: Tier applied to a site created without signing in.
type: string
contentDomain:
description: 'Apex domain a site''s subdomain sits under, for example `dropto.live`. Served here rather than
hardcoded in the frontend for the same reason every number above is: it is server configuration, and a
client that guessed it would show the wrong suffix the moment the domain changed.'
type: string
plans:
description: Every tier, smallest first.
items:
$ref: '#/components/schemas/PlanResponse'
type: array
signedInPlanId:
description: Tier applied once the visitor has an account.
type: string
required:
- plans
- anonymousPlanId
- signedInPlanId
- contentDomain
type: object
PlanResponse:
description: One quota tier. Field for field the `plans` row, so no mapping table is needed anywhere.
properties:
allowAnalyticsExport:
description: 'Whether the dashboard offers to save this tier''s traffic figures as files.
Another statement about what the tier is sold with rather than a lock: the files are assembled in
the browser from the traffic response the page already holds, so a reader who can see the page can
read the same numbers off it — see bool Plan.AllowAnalyticsExport.'
type: boolean
allowBrowserEdit:
description: 'Whether the dashboard lets an owner edit a file of their site in the browser.
A gate rather than packaging, like the flag above: the endpoint the editor reads a file through is
refused outright for a tier without it.'
type: boolean
allowFeedback:
description: 'Whether a site on this tier can take comments on its pages (docs/IMPLEMENTATION-PLAN.md §16.7).
Served so the pricing page and the marketing pages say which tiers include comments from the table,
never from a sentence: the tiers that carry it are set in the admin console and change without a release.'
type: boolean
allowInvitedViewers:
description: 'Whether a site on this tier may be shared with named people, each signing in with their own account.
A capability beside AllowPasswordProtection — the two are the paid answers to "who
can see this site" — and served for the same reason: the pricing page lists it beside the password.'
type: boolean
allowPasswordProtection:
description: 'Whether a site on this tier may be put behind a password. The first capability on this record rather
than a quantity, so it reads as a boolean instead of a limit of zero: the tier either offers the
feature or does not, and "0 passwords" would describe a countable resource nobody has any of.
Served here for the same reason every number above is — the pricing page and the site settings form
both have to know, and a client that hardcoded which tiers had it would be wrong the first time an
operator flipped the column.'
type: boolean
allowQrCode:
description: 'Whether the dashboard offers a QR code for the addresses of sites on this tier.
Served rather than decided in the browser for the same reason the flag above is, and for one more:
this is the only thing that decides the feature at all. The code is drawn from an address that is
already public, so the tier''s answer is a statement about what the tier is sold with, not a lock —
see bool Plan.AllowQrCode.'
type: boolean
allowScheduledExpiry:
description: 'Whether an owner on this tier may schedule when one of their sites comes down.
Unlike the two flags above, this one is a real gate rather than a statement about packaging: the
server refuses to set a date for a tier without it. Served all the same, so the pricing page can
list it and the dashboard can explain the absence rather than failing on submit.'
type: boolean
allowTrafficBreakdown:
description: 'Whether this tier''s traffic page lists top pages, referrers, countries and broken links, and filters
by them. Served so the pricing page and the locked panels can name the tier that includes them.'
type: boolean
allowTrafficDetail:
description: 'Whether this tier''s traffic page splits people from bots and AI, and shows devices, browsers,
campaigns and edge speed.'
type: boolean
analyticsDays:
description: 'How many days of a site''s own traffic this tier lets its owner see, or zero when it includes none.
A count where bool PlanResponse.AllowPasswordProtection is a flag, because there is something to meter
here: the difference between tiers is how far back the history reaches, and a boolean would need a
second field to say that. Zero reads as "not on this tier" rather than as a quantity nobody has,
which is why the pricing page renders it as words rather than as the number.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
badgeText:
description: Short label shown against the tier, or null for none. See string? Plan.BadgeText.
type:
- 'null'
- string
currency:
description: ISO 4217 code the two price fields are counted in.
type: string
displayOrder:
description: Where the tier sits on the pricing page, lowest first.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
id:
description: Plan id, as stored.
type: string
isExclusive:
description: 'Whether this tier is not on the pricing page, and reaches the caller only because it was agreed with
them.
Always false on the anonymous route, which returns the listed tiers and therefore has nothing to say
here. It exists so the dashboard can label the card: a reader meeting a price nobody else is offered,
beside prices everybody is, would otherwise have no way to tell which is which — and would reasonably
wonder why they cannot find it on the pricing page.
Not the same question as bool PlanResponse.IsPurchasable. A tier can be agreed before its prices are set,
which is exclusive and not yet buyable, and the card has to say both.'
type: boolean
isPurchasable:
description: 'Whether the tier can be bought right now. Separate from being listed: a tier that is listed and not
purchasable shows its price and its limits with no button, which is how a tier is announced before
there is a checkout behind it. See bool Plan.IsPurchasable.'
type: boolean
isTeam:
description: 'Whether the two prices are the cost of one seat rather than of one period.
Served publicly because the pricing page cannot render the tier without it: fifteen dollars a seat
and fifteen dollars a period are the same number in the same field and three times apart in what a
customer pays. A page that showed the column would put this tier beside the one above it at an
identical price.'
type: boolean
lifetimeDays:
description: Days before the site is deleted, or null when it is kept for good.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
maxAccountBytes:
description: 'Maximum bytes the account may hold across every site and every kept version, or null when the tier
has no total. Null on the anonymous tier, which has no owner to total by. See
long? Plan.MaxAccountBytes.'
format: int64
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
maxAccountWriteOpsPerMonth:
description: 'New or changed files the account may publish in a calendar month, or null when the tier is not
bounded by month. The one limit on this page that is a cost rather than a capacity: republishing a
file that has not changed spends nothing against it.'
format: int64
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
maxCustomDomains:
description: 'Maximum hostnames of the customer''s own that one account may be served on, counted across every site
it owns. Zero means the tier does not include the feature, which is what both free tiers are seeded
with — so the pricing page reads this as a capability rather than as a quantity. Never null, unlike
the quotas above it: there is no unlimited version of a resource billed monthly per unit. Distinct
from int? PlanResponse.MaxCustomSubdomains, which counts names on our own content domain. See
int Plan.MaxCustomDomains.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxCustomSubdomains:
description: 'Maximum sites one owner may have picked their own subdomain for, or null when the tier does not limit
that at all. See int? Plan.MaxCustomSubdomains.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
maxFileBytes:
description: Maximum size of a single file, in bytes.
format: int64
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxFiles:
description: Maximum files in one deploy.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxFormSubmissionsPerMonth:
description: 'Maximum form submissions one account may take in a calendar month, across every site it owns. Zero
means the tier does not include the feature, and every tier is seeded zero — the figure worth selling
is not known yet, so it ships off and an operator raises it per row once the counter has something to
read. The pricing page therefore renders this as a capability until somebody sets a number. See
int Plan.MaxFormSubmissionsPerMonth.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxFunctionInvocationsPerMonth:
description: 'Function calls one account''s sites may make together in a calendar month, before seats. Zero means the tier
does not include functions. See int Plan.MaxFunctionInvocationsPerMonth.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxFunctionSites:
description: 'Sites of one account that may run functions at once (docs/briefs/FUNCTIONS-BRIEF.md D18). Zero, with
MaxFunctionInvocationsPerMonth, means the tier does not include functions — every tier sold
today, until an operator sets a figure on the row. See int Plan.MaxFunctionSites.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxProxyRequestsPerMonth:
description: 'Requests one account''s sites may proxy to their owners'' upstreams in a calendar month. Zero means the tier
does not include the proxy. See int Plan.MaxProxyRequestsPerMonth.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxSeats:
description: Most seats it can be held with, or null for no ceiling.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
maxSiteBytes:
description: 'Maximum total size of one deploy of one site, in bytes, or null when the tier does not bound a single
publish and lets the account total do it instead. Not a storage limit either way: it is weighed
against a single publish, and every kept version is stored whole. MaxAccountBytes
is the storage one.'
format: int64
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
maxSiteComments:
description: 'The most comments one site on this tier may hold, deleted ones not counted. Only meaningful where
AllowFeedback is on; served on every tier like the other ceilings.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxSiteViewers:
description: 'How many people one invite-only site may list, its owner included. Read only where
AllowInvitedViewers is on; a figure on a tier without the feature describes nothing.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
maxSites:
description: 'Maximum sites one owner may have, or null when the tier has no owner to count by — the anonymous tier,
whose sites belong to nobody.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
minSeats:
description: Fewest seats the tier is sold with, which is what turns a seat price into an entry price.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
name:
description: 'What the tier is called on screen, falling back to the id when a row has never been named. Served
rather than written into the pricing page so that a tier added to the table appears with its own name
instead of an id, which is what makes a new tier a database insert rather than a release.'
type: string
parentPlanId:
description: 'The tier this one is built on, or null when it stands alone.
Every quota above is already the effective one — inheritance is materialised on the row, so nothing
reading this response has to resolve anything. This field is here for what the pricing page says
rather than for what it computes: a tier built on another is listed as everything that one includes
plus what it adds, and the rows that repeat the parent''s figures are left off the card. Without the
link the page would have to guess that relationship from the numbers, and two tiers that happen to
share a figure are not the same claim as one built on the other.
Null on the card when the parent is not itself public: there is no listed tier to name, so the card
falls back to listing every row.'
type:
- 'null'
- string
perSeatAccountBytes:
description: 'Storage each seat past the minimum adds, or null when storage does not scale with seats.
The per-seat figures are served for one reason, and it is not decoration. This endpoint is
public, so it has no account and therefore no seat count — it can only state the tier''s base. The
dashboard states the effective figure for the account reading it. Without the delta beside the base,
the two pages say different numbers for the same plan and the promise that the pricing page shows
what the service enforces stops being true.'
format: int64
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
perSeatMaxCustomDomains:
description: 'Hostnames of the customer''s own each seat past the minimum adds. Zero says a seat buys none, which
is where every tier starts and what the free tiers stay at.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
perSeatMaxFormSubmissionsPerMonth:
description: 'Form submissions a month each seat past the minimum adds to the account''s allowance. Zero says a
seat buys none, which is every tier but Team.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
perSeatMaxFunctionInvocationsPerMonth:
description: Function calls a month each seat past the minimum adds. Zero says a seat buys none.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
perSeatMaxProxyRequestsPerMonth:
description: Proxied requests a month each seat past the minimum adds. Zero says a seat buys none.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
perSeatMaxSites:
description: Sites each seat past the minimum adds, or null when they do not scale.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
perSeatWriteOpsPerDay:
description: Daily budget each seat adds, or null when it does not scale.
format: int64
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
perSeatWriteOpsPerMonth:
description: Monthly budget each seat adds, or null when it does not scale.
format: int64
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
priceCentsMonth:
description: 'Price of one month in the smallest unit of Currency, or null when the tier is not
sold by the month. Null rather than zero on the free tiers: they are not priced at zero, they are not
on sale.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
priceCentsYear:
description: Price of one year, or null when the tier is not sold by the year.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
prioritySupport:
description: 'Whether support requests from accounts on this tier are answered ahead of the queue.
Served because the pricing page lists it, and for no other reason: nothing in the API branches on
it. It is the one field here that no endpoint enforces, because what it describes is a promise kept
by whoever answers the mail — see bool Plan.PrioritySupport.'
type: boolean
retainedDeploys:
description: How many recent deploys stay available.
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- integer
- string
showsBadge:
description: 'Whether sites on this tier carry the Drop2Run badge in their served HTML.
The one field on this record that describes something the platform does *to* a site rather than
something the site is allowed to do, which is why it reads as "shows" rather than "allows". The
pricing page states it as a row like any other limit, because it is a difference between the tiers a
reader has to know before they upload rather than after.'
type: boolean
sitesPerIpPerDay:
description: 'Maximum new sites one network may create per day, or null when the tier is not counted by address.
Abuse protection rather than a product limit, so nothing is expected to display it.'
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
tagline:
description: One line under the name, or null to show none. See string? Plan.Tagline.
type:
- 'null'
- string
required:
- id
- maxSites
- maxSiteBytes
- maxAccountBytes
- maxAccountWriteOpsPerMonth
- maxFiles
- maxFileBytes
- retainedDeploys
- lifetimeDays
- sitesPerIpPerDay
- maxCustomSubdomains
- maxCustomDomains
- maxFormSubmissionsPerMonth
- maxProxyRequestsPerMonth
- allowPasswordProtection
- allowInvitedViewers
- maxSiteViewers
- allowQrCode
- allowAnalyticsExport
- allowScheduledExpiry
- allowBrowserEdit
- allowFeedback
- maxSiteComments
- showsBadge
- prioritySupport
- analyticsDays
- allowTrafficBreakdown
- allowTrafficDetail
- name
- tagline
- badgeText
- priceCentsMonth
- priceCentsYear
- currency
- isPurchasable
- displayOrder
- parentPlanId
- isTeam
- minSeats
- maxSeats
- perSeatAccountBytes
- perSeatMaxSites
- perSeatWriteOpsPerMonth
- perSeatWriteOpsPerDay
- perSeatMaxCustomDomains
- perSeatMaxFormSubmissionsPerMonth
- perSeatMaxProxyRequestsPerMonth
- isExclusive
- maxFunctionSites
- maxFunctionInvocationsPerMonth
- perSeatMaxFunctionInvocationsPerMonth
type: object
ProblemDetails:
properties:
detail:
type:
- 'null'
- string
instance:
type:
- 'null'
- string
status:
format: int32
pattern: ^-?(?:0|[1-9]\d*)$
type:
- 'null'
- integer
- string
title:
type:
- 'null'
- string
type:
type:
- 'null'
- string
type: object
securitySchemes:
accessToken:
description: 'A personal access token, created at /account/tokens. Sent as `Authorization: Bearer d2r_...`. A rejected token answers 401 with `invalid_token`, which is deliberately distinct from the 403 a valid credential gets for something it may not do.'
scheme: bearer
type: http
claimToken:
description: Returned once when an anonymous site is created, and the only credential that site has until somebody signs in and claims it.
in: header
name: X-Claim-Token
type: apiKey
session:
description: The browser session, set by signing in with Google. HttpOnly, so a script cannot read it and a client outside a browser cannot send it.
in: cookie
name: d2r_session
type: apiKey