Aftermarket
Learn more: Aftermarket API — marketing overview, live demo, and use cases.
Check if a domain is listed for sale on aftermarket marketplaces. Aggregates real-time data from multiple providers using a pluggable multi-source architecture.
Endpoint
GET https://api.robotdomainsearch.com/v1/aftermarket/{domain}
The domain is a path parameter (e.g., /v1/aftermarket/example.com).
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
sources |
string | (all enabled) | Comma-separated list of sources to query. Example: ?sources=sedo,dynadot |
timeout |
int | 5000 |
Per-source timeout in milliseconds. Range: 1–15000. Values above 15000 are capped to 15000. |
currency |
string | USD |
Preferred display currency (Sedo prices auto-convert EUR→USD). |
Request
Basic — Check all sources
curl https://api.robotdomainsearch.com/v1/aftermarket/premium.com
Filter to specific source
curl "https://api.robotdomainsearch.com/v1/aftermarket/premium.com?sources=sedo"
Multiple sources with custom timeout
curl "https://api.robotdomainsearch.com/v1/aftermarket/premium.com?sources=sedo,dynadot&timeout=8000"
Response
{
"domain": "example.com",
"timestamp": "2026-02-14T12:00:00Z",
"listed": true,
"summary": {
"totalSources": 2,
"sourcesChecked": 2,
"sourcesWithData": 2,
"listedCount": 2,
"lowestPrice": {
"amount": 2500.00,
"currency": "USD",
"source": "dynadot",
"saleType": "buyNow"
},
"hasAuction": false,
"hasMakeOffer": false,
"hasBackorder": false
},
"listings": [
{
"source": "sedo",
"platform": "Sedo",
"status": "listed",
"saleType": "buyNow",
"price": {
"amount": 2700.00,
"currency": "USD"
},
"makeOffer": false,
"auction": false,
"url": "https://sedo.com/search/details/?domain=example.com",
"affiliateUrl": "https://sedo.com/search/details/?domain=example.com&campaignId=330835",
"lastChecked": "2026-02-14T12:00:00Z",
"responseMs": 450
},
{
"source": "dynadot",
"platform": "Dynadot Marketplace",
"status": "listed",
"saleType": "buyNow",
"price": {
"amount": 2500.00,
"currency": "USD"
},
"makeOffer": false,
"auction": false,
"url": "https://www.dynadot.com/market/auction/example.com",
"affiliateUrl": "https://www.dynadot.com/market/auction/example.com",
"lastChecked": "2026-02-14T12:00:00Z",
"responseMs": 320
}
],
"errors": [],
"meta": {
"totalMs": 455,
"affiliateDisclosure": "Links may earn RobotDomainSearch a commission at no extra cost to you.",
"cacheHit": false,
"cacheTTL": 900
}
}
Response Fields
Top-Level Fields
| Field | Type | Description |
|---|---|---|
domain |
string | The queried domain name (lowercased) |
timestamp |
ISO 8601 | When the response was generated |
listed |
boolean | true if the domain is listed on any source |
summary |
object | Aggregated overview across all sources |
listings |
array | Per-source listing details |
errors |
array | Per-source errors (partial failures) |
meta |
object | Request metadata |
Listing Object
| Field | Type | Description |
|---|---|---|
source |
string | Provider identifier (sedo, dynadot) |
platform |
string | Human-readable name (Sedo, Dynadot Marketplace) |
status |
enum | listed, notListed, auction, backorder, error |
saleType |
enum | buyNow, makeOffer, auction, backorder |
price |
object? | { amount: number, currency: string } — null if make-offer only |
makeOffer |
boolean | Whether the seller accepts offers |
auction |
boolean | Whether the domain is in an active auction |
auctionDetails |
object? | Auction end time, bid count, reserve status (when applicable) |
url |
string | Direct link to the listing page |
affiliateUrl |
string | Affiliate/partner link (earns RobotDomainSearch commission) |
lastChecked |
ISO 8601 | When this source was queried |
responseMs |
number | Source response time in milliseconds |
Summary Object
| Field | Type | Description |
|---|---|---|
totalSources |
number | Total registered providers |
sourcesChecked |
number | Providers queried (including errored) |
sourcesWithData |
number | Providers that returned valid data |
listedCount |
number | How many sources have the domain listed |
lowestPrice |
object? | Cheapest listing: { amount, currency, source, saleType } |
hasAuction |
boolean | Any source has an active auction |
hasMakeOffer |
boolean | Any source accepts offers |
hasBackorder |
boolean | Any source supports backorder |
Error Object
| Field | Type | Description |
|---|---|---|
source |
string | Which provider errored |
code |
string | Error code (see below) |
message |
string | Human-readable error description |
retryable |
boolean | Whether the request can be retried |
Meta Object
| Field | Type | Description |
|---|---|---|
totalMs |
number | Total response time in milliseconds |
affiliateDisclosure |
string | Disclosure notice about affiliate links |
cacheHit |
boolean | Whether the response was served from cache |
cacheTTL |
number | Cache TTL in seconds for this response |
Domain Not Listed
When a domain is not listed on any marketplace:
{
"domain": "my-random-domain-12345.com",
"listed": false,
"summary": {
"totalSources": 2,
"sourcesChecked": 2,
"sourcesWithData": 2,
"listedCount": 0,
"hasAuction": false,
"hasMakeOffer": false,
"hasBackorder": false
},
"listings": [
{
"source": "sedo",
"platform": "Sedo",
"status": "notListed",
"lastChecked": "2026-02-14T12:00:00Z",
"responseMs": 380
},
{
"source": "dynadot",
"platform": "Dynadot Marketplace",
"status": "notListed",
"lastChecked": "2026-02-14T12:00:00Z",
"responseMs": 210
}
],
"errors": [],
"meta": { "totalMs": 385, "cacheHit": false, "cacheTTL": 3600 }
}
Error Handling
Error Codes
| Code | Meaning | Retryable |
|---|---|---|
not_configured |
Source not registered or missing credentials | No |
timeout |
Source didn’t respond within the timeout | Yes |
rate_limited |
Source returned 429 | Yes |
auth_error |
Invalid credentials (401/403) | No |
unavailable |
General source failure | Yes |
Partial Failures
The endpoint is designed for graceful degradation. If one source fails, others still return data:
{
"domain": "example.com",
"listed": true,
"listings": [
{ "source": "dynadot", "status": "listed", "...": "..." }
],
"errors": [
{
"source": "sedo",
"code": "timeout",
"message": "sedo: request failed: context deadline exceeded",
"retryable": true
}
]
}
HTTP Status Codes
| Status | Condition |
|---|---|
200 |
Success (including partial failures with some data) |
400 |
Invalid domain format |
405 |
Method not allowed (only GET supported) |
501 |
All requested sources are not_configured |
503 |
All sources failed with retryable errors / service not initialized |
Requesting an Unknown Source
curl "https://api.robotdomainsearch.com/v1/aftermarket/example.com?sources=afternic"
Returns 501 with:
{
"errors": [{
"source": "afternic",
"code": "not_configured",
"message": "Source 'afternic' is not configured",
"retryable": false
}]
}
Caching
Responses are cached in-memory with variable TTLs based on listing status:
| Listing Status | Cache TTL |
|---|---|
| Listed with price | 15 minutes |
| Listed (make offer) | 30 minutes |
| Active auction | 5 minutes |
| Not listed | 1 hour |
| Error / no data | 30 seconds |
- Cache key is based on
domain + sorted source list meta.cacheHitindicates whether the response was served from cachemeta.cacheTTLshows the TTL in seconds for the current response- LRU eviction at 10,000 entries max
Architecture: Pluggable Multi-Source Design
RobotDomainSearch’s aftermarket endpoint uses a fan-out architecture:
- All enabled providers are queried in parallel using Go’s
errgroup - Each provider has an independent per-source timeout
- Results are aggregated into a single response with per-source detail
- Partial failures are captured in
errors[]— successful sources still return data - New providers can be added by implementing the
AftermarketClientinterface (1 file + 1 registration line)
Current Providers (Phase 1)
| Source | Platform | API Type | Notes |
|---|---|---|---|
sedo |
Sedo | SOAP/XML | EUR→USD auto-conversion; requires partner ID + sign key |
dynadot |
Dynadot Marketplace | REST/JSON | get_listing_item command; requires API key |
Coming Soon
Additional providers planned for future releases:
- Afternic (GoDaddy’s aftermarket network)
- Dan.com
- Atom.com (NameCheap)
- Squadhelp
Domain Validation
- Domains are lowercased and trimmed
- Must match pattern:
^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$ - Max length: 253 characters
- Invalid domains return
400withinvalid_domainerror
Affiliate Disclosure
All responses include meta.affiliateDisclosure. Listing URLs may contain affiliate/partner parameters. The affiliateUrl field contains the affiliate link, while url is the direct listing link.
Code Examples
curl
curl https://api.robotdomainsearch.com/v1/aftermarket/premium.com
Python
import requests
response = requests.get(
"https://api.robotdomainsearch.com/v1/aftermarket/premium.com"
)
data = response.json()
print(f"Domain: {data['domain']}")
print(f"Listed: {data['listed']}")
if data["listed"]:
summary = data["summary"]
print(f"Sources with listings: {summary['listedCount']}")
if summary.get("lowestPrice"):
lp = summary["lowestPrice"]
print(f"Lowest price: ${lp['amount']:.2f} ({lp['source']})")
for listing in data["listings"]:
if listing["status"] == "listed":
price = listing.get("price", {})
print(
f" {listing['platform']}: ${price.get('amount', '?')} "
f"({listing['saleType']})"
)
for error in data.get("errors", []):
print(f" Error [{error['source']}]: {error['message']}")
JavaScript
const domain = "premium.com";
const response = await fetch(
`https://api.robotdomainsearch.com/v1/aftermarket/${domain}`
);
const data = await response.json();
console.log(`Domain: ${data.domain}`);
console.log(`Listed: ${data.listed}`);
if (data.listed) {
const { summary } = data;
console.log(`Sources with listings: ${summary.listedCount}`);
if (summary.lowestPrice) {
const lp = summary.lowestPrice;
console.log(`Lowest: $${lp.amount} (${lp.source})`);
}
data.listings
.filter(l => l.status === "listed")
.forEach(l => {
console.log(` ${l.platform}: $${l.price?.amount} (${l.saleType})`);
});
}
data.errors?.forEach(e => {
console.log(` Error [${e.source}]: ${e.message}`);
});
Go
resp, err := http.Get("https://api.robotdomainsearch.com/v1/aftermarket/premium.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var data struct {
Domain string `json:"domain"`
Listed bool `json:"listed"`
Summary struct {
ListedCount int `json:"listedCount"`
LowestPrice *struct {
Amount float64 `json:"amount"`
Currency string `json:"currency"`
Source string `json:"source"`
} `json:"lowestPrice,omitempty"`
} `json:"summary"`
Listings []struct {
Source string `json:"source"`
Platform string `json:"platform"`
Status string `json:"status"`
SaleType string `json:"saleType,omitempty"`
Price *struct {
Amount float64 `json:"amount"`
Currency string `json:"currency"`
} `json:"price,omitempty"`
} `json:"listings"`
Errors []struct {
Source string `json:"source"`
Code string `json:"code"`
Message string `json:"message"`
} `json:"errors"`
}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
log.Fatal(err)
}
fmt.Printf("Domain: %s (listed: %t)\n", data.Domain, data.Listed)
if data.Summary.LowestPrice != nil {
fmt.Printf("Lowest: $%.2f (%s)\n",
data.Summary.LowestPrice.Amount,
data.Summary.LowestPrice.Source)
}
for _, l := range data.Listings {
if l.Status == "listed" && l.Price != nil {
fmt.Printf(" %s: $%.2f (%s)\n", l.Platform, l.Price.Amount, l.SaleType)
}
}
Notes
- The aftermarket endpoint requires the domain as a path parameter, not a query parameter
- No API key is required during beta (60 requests/minute rate limit)
- Sedo prices are returned in EUR and automatically converted to USD
- The
affiliateUrlearns RobotDomainSearch a commission at no extra cost to the buyer - Responses include both successful results and partial errors in a single response
- Cache TTLs vary by listing status to balance freshness with performance