Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
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.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/justdomain-ai:justdomain-ai-transfer-api"
All apis
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.
Get an API 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: just-domain-mcp Transfer API
version: 0.1.0
tags:
- name: Transfer
paths:
/api/transfer/precheck:
post:
summary: Post Transfer Precheck
description: Can this domain move to us, what stops it, and what would it cost?
operationId: post_transfer_precheck_api_transfer_precheck_post
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TransferPrecheckRequest'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/TransferPrecheckResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Transfer
get:
summary: Get Transfer Precheck
description: The same answer as the POST, in a shape an agent can cite.
operationId: get_transfer_precheck_api_transfer_precheck_get
parameters:
- name: domain
in: query
required: true
schema:
type: string
minLength: 3
maxLength: 253
description: The domain to check, e.g. 'example.com'.
title: Domain
description: The domain to check, e.g. 'example.com'.
- name: include_records
in: query
required: false
schema:
type: boolean
description: Also probe public DNS and return the records on `dns_continuity.records`. Off by default - it is ~57 lookups and the eligibility answer does not need them.
default: false
title: Include Records
description: Also probe public DNS and return the records on `dns_continuity.records`. Off by default - it is ~57 lookups and the eligibility answer does not need them.
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/TransferPrecheckResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Transfer
/api/transfer/dns-snapshot:
post:
summary: Post Transfer Dns Snapshot
description: 'The "does this look complete?" step, and the set that fills the zone.
Public and unauthenticated for the same reason the checker is: this runs
BEFORE anyone has an account, and a login wall in front of "show me what
my domain currently serves" loses exactly the customer the page exists to
find. It reads public DNS and the text the caller uploaded. It touches no
database and no order.
Three inputs merged in one place, most authoritative last: the probe,
then the uploaded BIND export (which came from the authoritative side and
beats every guess we can make), then whatever the customer typed.
**Everything is validated here with the SAME function the DNS screen
uses**, and a record that fails comes back in `invalid` with the reason
rather than being dropped. The client''s own validation is a convenience;
this is the gate. It is also where the MX-with-no-priority hole is
closed - a client computing `Number(null)` gets 0, which is a legal MX
priority, so an MX with no priority set would otherwise sail through as
the highest-priority mail route nobody chose.
Nothing here promises the set is COMPLETE. A probe can only ask for names
it thought of; `notes` says so in the first line, every time.'
operationId: post_transfer_dns_snapshot_api_transfer_dns_snapshot_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TransferDnsSnapshotRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/TransferDnsSnapshotResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
tags:
- Transfer
components:
schemas:
TransferPrecheckRequest:
properties:
domain:
type: string
maxLength: 253
minLength: 3
title: Domain
description: The domain to check, e.g. 'example.com'.
include_records:
type: boolean
title: Include Records
description: 'Also probe public DNS for the domain''s records and return them on `dns_continuity.records`. Off by default: it is roughly 57 lookups and the eligibility answer does not need them. The transfer wizard''s DNS step turns it on.'
default: false
type: object
required:
- domain
title: TransferPrecheckRequest
DnsRecordProblemOut:
properties:
record:
$ref: '#/components/schemas/DnsRecordOut'
reason:
type: string
title: Reason
type: object
required:
- record
- reason
title: DnsRecordProblemOut
description: A record we would not write, and the reason, in the customer's words.
TransferPrecheckResponse:
properties:
domain:
type: string
title: Domain
tld:
type: string
title: Tld
supported:
type: boolean
title: Supported
checked_at:
type: string
title: Checked At
blockers:
items:
$ref: '#/components/schemas/TransferBlockerOut'
type: array
title: Blockers
default: []
registrar:
$ref: '#/components/schemas/TransferRegistrarOut'
nameservers:
items:
type: string
type: array
title: Nameservers
default: []
dns_continuity:
$ref: '#/components/schemas/DnsContinuityOut'
price:
anyOf:
- $ref: '#/components/schemas/TransferPriceOut'
- type: 'null'
source:
$ref: '#/components/schemas/TransferPrecheckSourceOut'
ready:
type: boolean
title: Ready
description: 'TRUE only when `blockers` is empty. A convenience over that list that can never disagree with it: anything we could not establish becomes a blocker, so a registry timeout reads as not ready.'
transfer_url:
type: string
title: Transfer Url
description: The page on justdomain.ai a PERSON opens in a browser to take this further.
next_step:
type: string
title: Next Step
description: One sentence about what happens next, already matched to this domain's state. Written to be relayed to a person as-is.
auth_code_policy:
type: string
title: Auth Code Policy
description: Constant. The rule about authorization (EPP) codes, restated in every payload so it travels with the answer.
type: object
required:
- domain
- tld
- supported
- checked_at
- registrar
- dns_continuity
- source
- ready
- transfer_url
- next_step
- auth_code_policy
title: TransferPrecheckResponse
description: 'The public precheck payload. The frontend checker is built against it.
**Additive only.** Every key below `source` was added after the page
shipped, and nothing above it may be removed or renamed: the checker
reads the payload as a typed object, and a renamed key is a blank on a
public page. `tests/test_transfer_precheck_parity.py` pins the original
key set.
The last four fields are the ones the MCP tool `check_domain_transfer`
has always carried. They come from the same builder
(`shared/services/transfer_handoff`), so an agent with no MCP client gets
the same `ready`, the same handoff link, the same next step and - the
one that matters - the same authorization-code refusal, byte for byte.
The one key that still differs is the projected date: `new_expiry` here,
`expected_new_expiry` on the tool.'
TransferPrecheckSourceOut:
properties:
rdap:
type: boolean
title: Rdap
registrar_pricing:
type: boolean
title: Registrar Pricing
type: object
required:
- rdap
- registrar_pricing
title: TransferPrecheckSourceOut
description: 'Which sources actually answered on this request.
`registrar_pricing: false` means the price is missing because we could
not read it, not because the domain is free to transfer.'
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object
required:
- loc
- msg
- type
title: ValidationError
DnsRecordOut:
properties:
type:
type: string
title: Type
description: A, AAAA, CNAME, MX, TXT, NS, SRV, CAA.
name:
type: string
title: Name
description: The absolute host, e.g. 'example.com' or '_dmarc.example.com'.
value:
type: string
title: Value
ttl:
type: integer
title: Ttl
default: 3600
prio:
anyOf:
- type: integer
- type: 'null'
title: Prio
type: object
required:
- type
- name
- value
title: DnsRecordOut
description: 'One DNS record, in the five fields the rest of the product uses.
Identical in shape to what the DNS-management screen sends, so a record
read off somebody else''s nameservers and a record typed into our own
form are the same object and never need translating between the two.'
TransferRegistrarOut:
properties:
name:
anyOf:
- type: string
- type: 'null'
title: Name
iana_id:
anyOf:
- type: string
- type: 'null'
title: Iana Id
type: object
title: TransferRegistrarOut
description: Who sponsors the domain today, as the REGISTRY reports it.
TransferDnsSnapshotResponse:
properties:
domain:
type: string
title: Domain
checked_at:
type: string
title: Checked At
records:
items:
$ref: '#/components/schemas/DnsRecordOut'
type: array
title: Records
default: []
probed:
type: boolean
title: Probed
default: false
unreadable:
type: integer
title: Unreadable
default: 0
notes:
items:
type: string
type: array
title: Notes
default: []
invalid:
items:
$ref: '#/components/schemas/DnsRecordProblemOut'
type: array
title: Invalid
default: []
bind_errors:
items:
type: string
type: array
title: Bind Errors
default: []
bind_skipped:
items:
type: string
type: array
title: Bind Skipped
default: []
type: object
required:
- domain
- checked_at
title: TransferDnsSnapshotResponse
TransferDnsSnapshotRequest:
properties:
domain:
type: string
maxLength: 253
minLength: 3
title: Domain
probe:
type: boolean
title: Probe
description: Run the public-DNS probe. Off to validate an upload alone.
default: true
bind_zone:
anyOf:
- type: string
maxLength: 400000
- type: 'null'
title: Bind Zone
description: A BIND-format zone file, as most registrars export it. A documented subset is parsed; anything unreadable comes back in `bind_errors` rather than being dropped in silence.
records:
anyOf:
- items:
$ref: '#/components/schemas/DnsRecordOut'
type: array
maxItems: 400
- type: 'null'
title: Records
description: Records the customer added or corrected by hand.
type: object
required:
- domain
title: TransferDnsSnapshotRequest
description: 'Build the record set that will fill the pre-staged zone.
Three inputs, merged in this order so the most authoritative wins:
the public-DNS probe, then the uploaded BIND export, then anything the
customer typed. A zone file exported by the losing registrar beats every
guess our probe can make, which is why it overrides it.'
TransferPriceOut:
properties:
amount:
type: string
title: Amount
currency:
type: string
title: Currency
term_years:
type: integer
title: Term Years
term_note:
type: string
title: Term Note
new_expiry:
anyOf:
- type: string
- type: 'null'
title: New Expiry
is_premium:
type: boolean
title: Is Premium
type: object
required:
- amount
- currency
- term_years
- term_note
- is_premium
title: TransferPriceOut
TransferBlockerOut:
properties:
code:
type: string
title: Code
detail:
type: string
title: Detail
eligible_at:
anyOf:
- type: string
- type: 'null'
title: Eligible At
type: object
required:
- code
- detail
title: TransferBlockerOut
DnsContinuityOut:
properties:
case:
type: string
title: Case
provider:
anyOf:
- type: string
- type: 'null'
title: Provider
detail:
type: string
title: Detail
records:
items:
$ref: '#/components/schemas/DnsRecordOut'
type: array
title: Records
default: []
records_probed:
type: boolean
title: Records Probed
default: false
records_unreadable:
type: integer
title: Records Unreadable
default: 0
records_notes:
items:
type: string
type: array
title: Records Notes
default: []
type: object
required:
- case
- detail
title: DnsContinuityOut
description: 'Does DNS keep working after the transfer?
"independent" - the nameservers belong to a DNS provider, not to the
losing registrar, so a transfer does not touch them. "losing_registrar" -
the dangerous case: the old registrar eventually stops serving the zone.
"unknown" - we do not recognise the nameservers, said plainly.
`records` is the public-DNS probe the wizard renders for its "does this
look complete?" review. It is only populated when the caller asks for it
(`include_records`), because the probe is ~57 lookups against public
resolvers and the checker page does not need them to answer its own
question. **`records_probed` is how a client tells "we did not look" from
"there is nothing there"** - an empty `records` with `records_probed:
false` means the first, and treating it as the second is how somebody
stages an empty zone.'