REST API v1
Quick Start
All endpoints return JSON and work directly in browsers.
# Health check
curl https://adastack.io/api/v1/health
# List items
curl "https://adastack.io/api/v1/items?limit=20"
# Filter by category
curl "https://adastack.io/api/v1/items?category=browser-wallets"
# Search
curl "https://adastack.io/api/v1/items?search=staking"
# Single item
curl https://adastack.io/api/v1/items/eternl-browser-wallet
# Get all filter options
curl https://adastack.io/api/v1/taxonomiesEndpoints
Endpoints by Use Case
Content
- GET /api/v1/items - Search, filter, and paginate directory listings
- GET /api/v1/items/[slug] - Full details for a single item
Reference
- GET /api/v1/health - API status and cache health
- GET /api/v1/taxonomies - All filter options (sections, categories, languages)
GET /api/v1/items
List directory items with filtering, search, and pagination.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (1-10,000) |
limit | integer | 20 | Items per page (1-100) |
sort | string | name | Sort field: name, updatedAt, or createdAt |
order | string | - | asc or desc (default: asc for name, desc for date sorts) |
category | string | - | Filter by category slug |
section | string | - | Filter by section/group slug |
siteLanguage | string | - | Filter by site language slug |
programmingLanguage | string | - | Filter by programming language slug |
collection | string | - | Filter by collection slug |
search | string | - | Search name and description (2-100 chars) |
Multiple filters combine with AND logic. Use GET /api/v1/taxonomies to discover valid filter slugs.
Search Behavior
Search uses contains-matching (
*term*), not prefix matching:
search=lacematches "Lace wallet" AND "marketplace"search=swapmatches "SundaeSwap", "Minswap", "swap tokens"For precise results, combine with filters:
?search=lace&category=browser-wallets
Search is case-insensitive and matches anywhere within the name, mini description, or full description fields. No fuzzy matching, stemming, or boolean operators. URL-encode spaces as %20 or +.
curl "https://adastack.io/api/v1/items?limit=20"Response 200 OK
{
"items": [
{
"name": "Eternl Browser Wallet",
"slug": "eternl-browser-wallet",
"createdAt": "2024-06-15T08:00:00.000Z",
"category": {
"name": "Browser Wallets",
"slug": "browser-wallets",
"adastackURL": "https://www.adastack.io/category/browser-wallets"
},
"section": {
"name": "Wallets",
"slug": "wallets",
"adastackURL": "https://www.adastack.io/section/wallets"
},
"miniDescription": "Feature-rich Cardano light wallet",
"description": "Full description text...",
"introduction": "Brief intro paragraph...",
"websiteURL": "https://eternl.io",
"blogURL": null,
"whitepaperURL": null,
"xURL": "https://x.com/eternaborado",
"discordURL": "https://discord.gg/eternl",
"telegramURL": null,
"linkedinURL": null,
"facebookURL": null,
"youtubeURL": null,
"teamGithubURL": null,
"projectGithubURL": null,
"repoPrimaryLang": null,
"siteLanguage": null,
"collections": [],
"iconURL": "https://cdn.sanity.io/images/.../icon.png?w=200&h=200&fit=max&auto=format",
"imageURL": "https://cdn.sanity.io/images/.../image.png?w=1920&h=1080&fit=max&auto=format",
"adastackURL": "https://www.adastack.io/item/eternl-browser-wallet"
},
"..."
],
"metadata": {
"total": 127,
"page": 1,
"limit": 20,
"totalPages": 7,
"hasMore": true,
"firstPage": "https://www.adastack.io/api/v1/items?limit=20&category=browser-wallets&page=1",
"lastPage": "https://www.adastack.io/api/v1/items?limit=20&category=browser-wallets&page=7",
"nextPage": "https://www.adastack.io/api/v1/items?limit=20&category=browser-wallets&page=2",
"previousPage": null,
"sort": {
"field": "name",
"order": "asc"
},
"filters": {
"category": "browser-wallets"
},
"generated": "2025-01-15T12:00:00.000Z"
}
}Item Fields
| Field | Type | Description |
|---|---|---|
name | string | Display name (always present) |
slug | string | URL-safe identifier (always present) |
createdAt | string | ISO-8601 timestamp of initial creation |
category | object | null | {name, slug, adastackURL} |
section | object | null | {name, slug, adastackURL} |
miniDescription | string | null | One-liner description |
description | string | null | Full description |
introduction | string | null | Intro paragraph |
websiteURL | string | null | Main website |
blogURL | string | null | Blog URL |
whitepaperURL | string | null | Whitepaper/docs URL |
xURL | string | null | X (Twitter) profile |
discordURL | string | null | Discord invite |
telegramURL | string | null | Telegram group/channel |
linkedinURL | string | null | LinkedIn page |
facebookURL | string | null | Facebook page |
youtubeURL | string | null | YouTube channel |
teamGithubURL | string | null | GitHub org/team URL |
projectGithubURL | string | null | GitHub repo URL |
repoPrimaryLang | object | null | {name, slug, miniDescription, adastackURL} |
siteLanguage | object | null | {name, slug, miniDescription, adastackURL} |
collections | array | [{name, slug, miniDescription, adastackURL}] (may be empty) |
iconURL | string | null | Square logo (200x200, auto-optimized) |
imageURL | string | null | Hero image (1920x1080, auto-optimized) |
adastackURL | string | Item page URL (always present) |
Metadata Fields
| Field | Type | Description |
|---|---|---|
total | integer | Total items matching filters |
page | integer | Current page number |
limit | integer | Items per page |
totalPages | integer | Total pages available |
hasMore | boolean | Whether more pages exist |
firstPage | string | null | URL for first page, or null when no results |
lastPage | string | null | URL for last page, or null when no results |
nextPage | string | null | URL for next page, or null on last page |
previousPage | string | null | URL for previous page, or null on first page |
sort | object | Applied sort {field, order} |
filters | object | Applied filters including search term (empty object {} when no filters applied) |
generated | string | ISO 8601 timestamp |
Requesting a page beyond totalPages returns 200 OK with an empty items array.
Examples
# Paginate
curl "https://adastack.io/api/v1/items?page=2&limit=50"
# Filter by category
curl "https://adastack.io/api/v1/items?category=browser-wallets"
# Filter by section
curl "https://adastack.io/api/v1/items?section=dapps"
# Combine filters with search
curl "https://adastack.io/api/v1/items?category=dexs&search=liquidity"
# Sort by recently updated
curl "https://adastack.io/api/v1/items?sort=updatedAt"GET /api/v1/items/[slug]
Get a single directory item by slug.
curl https://adastack.io/api/v1/items/eternl-browser-walletResponse 200 OK
{
"item": {
"name": "Eternl Browser Wallet",
"slug": "eternl-browser-wallet",
"createdAt": "2024-06-15T08:00:00.000Z",
"category": { "name": "Browser Wallets", "slug": "browser-wallets", "adastackURL": "..." },
"section": { "name": "Wallets", "slug": "wallets", "adastackURL": "..." },
"miniDescription": "Feature-rich Cardano light wallet",
"description": "Full description text...",
"introduction": "Brief intro paragraph...",
"websiteURL": "https://eternl.io",
"xURL": "https://x.com/eternaborado",
"discordURL": "https://discord.gg/eternl",
"collections": [],
"iconURL": "https://cdn.sanity.io/images/.../icon.png?w=200&h=200&fit=max&auto=format",
"imageURL": "https://cdn.sanity.io/images/.../image.png?w=1920&h=1080&fit=max&auto=format",
"adastackURL": "https://www.adastack.io/item/eternl-browser-wallet"
},
"metadata": {
"generated": "2025-01-15T12:00:00.000Z"
}
}Same item fields as the list endpoint. Null fields omitted from example above for brevity.
Returns 404 if the slug does not match any item.
GET /api/v1/health
API status and endpoint discovery. Rate limit: 5,000 req/min. No caching.
curl https://adastack.io/api/v1/healthResponse 200 OK
{
"status": "ok",
"statsStatus": "ok",
"version": "v1",
"timestamp": "2025-01-15T12:00:00.000Z",
"stats": {
"totalItems": 1200,
"totalCategories": 108,
"totalSections": 15
},
"rateLimit": {
"dataEndpoints": {
"requestsPerMinute": 100
},
"healthEndpoint": {
"requestsPerMinute": 5000
},
"policy": "per-ip"
},
"endpoints": [
"/api/v1/items",
"/api/v1/items/[slug]",
"/api/v1/taxonomies",
"/api/v1/health"
],
"documentation": "/developers/api/v1"
}Response Fields
| Field | Type | Description |
|---|---|---|
status | string | Always "ok" if the API is responding |
statsStatus | string | "ok" if stats loaded, "unavailable" if CMS query failed |
stats | object | null | Directory statistics (null when statsStatus is "unavailable") |
The stats object may be null when the CMS is temporarily unavailable. The health endpoint remains functional even when stats cannot be fetched -- check statsStatus to determine if stats are current.
GET /api/v1/taxonomies
All taxonomy and reference data. Use this to discover valid slugs for filtering /api/v1/items.
curl https://adastack.io/api/v1/taxonomiesQuery Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
sort | string | alphabetical | alphabetical (name asc) or curated (priority order matching website) |
Response 200 OK
{
"sections": [
{
"name": "DApps",
"slug": "dapps",
"miniDescription": "Decentralized applications on Cardano",
"description": "Decentralized applications built on Cardano",
"updatedAt": "2025-01-10T08:00:00.000Z",
"itemCount": 204,
"adastackURL": "https://www.adastack.io/section/dapps"
},
"..."
],
"categories": [
{
"name": "Browser Wallets",
"slug": "browser-wallets",
"miniDescription": "Browser extension wallets",
"description": "Browser extension wallets for Cardano",
"updatedAt": "2025-01-12T09:30:00.000Z",
"section": {
"name": "Wallets",
"slug": "wallets",
"adastackURL": "https://www.adastack.io/section/wallets"
},
"itemCount": 9,
"adastackURL": "https://www.adastack.io/category/browser-wallets"
},
"..."
],
"languages": [
{
"name": "French",
"slug": "french",
"miniDescription": "French language resources",
"description": "Items available in French",
"updatedAt": "2025-01-08T14:20:00.000Z",
"itemCount": 10,
"adastackURL": "https://www.adastack.io/languages/french"
},
"..."
],
"programmingLanguages": [
{
"name": "Haskell",
"slug": "haskell",
"miniDescription": "Haskell programming language",
"description": "Functional programming language used for Cardano development",
"updatedAt": "2025-01-08T14:20:00.000Z",
"itemCount": 15,
"adastackURL": "https://www.adastack.io/programming-languages/haskell"
},
"..."
],
"metadata": {
"totalSections": 14,
"totalCategories": 106,
"totalLanguages": 26,
"totalProgrammingLanguages": 18,
"sort": {
"field": "name",
"order": "asc"
},
"generated": "2025-01-15T12:00:00.000Z"
}
}Each taxonomy object includes name, slug, miniDescription, description, updatedAt, itemCount, and adastackURL. Categories also include a section parent object. The sort field uses {field: "name", order: "asc"} for alphabetical and {field: "curated", order: "desc"} for curated.
Supports conditional requests via ETags (see Caching).
Rate Limiting
Limit: 100 requests/minute per IP (rolling 60-second window). Health endpoint: 5,000 req/min.
All responses include rate limit headers:
IETF Standard Headers (RFC draft-ietf-httpapi-ratelimit-headers)
| Header | Example | Description |
|---|---|---|
RateLimit | limit=100, remaining=95, reset=45 | Current limit, remaining requests, seconds until reset |
RateLimit-Policy | 100;w=60 | Limit and window size (60 seconds) |
Legacy Headers (maintained for backward compatibility)
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when limit resets |
When rate limited, you receive 429 Too Many Requests with a Retry-After header and retryAfter in the response body. Implement exponential backoff and respect the Retry-After value.
Caching
All data endpoints are cached at the edge. Cache durations vary by endpoint based on how frequently the data changes:
| Endpoint | Cache-Control | ETag |
|---|---|---|
/items, /items/[slug] | public, max-age=3600 (1 hour) | No |
/taxonomies | public, max-age=86400 (24 hours) | Yes |
/health | no-store | No |
Taxonomies use longer caching because sections and categories change infrequently compared to item data.
New content may take up to 1 hour to appear in most API responses (24 hours for taxonomies).
Conditional Requests (ETag)
The /api/v1/taxonomies endpoint supports conditional requests via ETags. This endpoint returns an ETag header with each response. On subsequent requests, include the ETag value in an If-None-Match header to receive a 304 Not Modified response when the data hasn't changed, saving bandwidth.
# First request - get the ETag from response headers
curl -i https://adastack.io/api/v1/taxonomies
# Response includes: ETag: "abc123..."
# Subsequent request - send ETag as If-None-Match
curl -i -H 'If-None-Match: "abc123..."' https://adastack.io/api/v1/taxonomies
# Returns 304 Not Modified (no body) if data unchangedCORS headers are configured to allow browsers to read the ETag header and send If-None-Match in cross-origin requests.
Error Handling
All errors return a consistent JSON structure:
{
"error": {
"code": "error_code",
"message": "Human-readable description",
"details": {}
}
}Error Codes
| Code | HTTP | Description | Resolution |
|---|---|---|---|
invalid_query | 400 | Invalid query parameters | Check parameter types and ranges |
invalid_slug | 400 | Malformed slug format | Use lowercase alphanumeric with hyphens |
not_found | 404 | Resource doesn't exist | Verify slug spelling |
unknown_category | 422 | Category slug not found | Use /api/v1/taxonomies for valid slugs |
unknown_section | 422 | Section slug not found | Use /api/v1/taxonomies for valid slugs |
unknown_language | 422 | Language slug not found | Use /api/v1/taxonomies for valid slugs |
unknown_programming_language | 422 | Programming language slug not found | Use /api/v1/taxonomies for valid slugs |
unknown_collection | 422 | Collection slug not found | Use /api/v1/taxonomies for valid slugs |
rate_limit_exceeded | 429 | Too many requests | Wait for Retry-After header value |
query_failed | 500 | Database query failed | Retry; contact support if persistent |
internal_error | 500 | Server error | Retry; contact support if persistent |
In production, error details contain only a documentation link. Development responses include full validation details.
Technical Reference
CORS
All origins are allowed (public read-only API). All endpoints include:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, If-None-Match
Access-Control-Expose-Headers: ETag, RateLimit, RateLimit-Policy, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
Security Headers
All responses include standard security headers:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
X-API-Version: v1
Response Format
All responses return JSON with Content-Type: application/json.
Response adastackURL fields use the canonical https://www.adastack.io domain.
Changelog
v1.0.8 (2026-02-05)
Item Responses:
- BREAKING: Removed
updatedAtfield from item responses (internal CMS metadata not appropriate for public API; sorting byupdatedAtstill works)
v1.0.7 (2026-02-05)
Data Quality:
- All string fields (names, descriptions) are now defensively trimmed to remove leading/trailing whitespace from CMS data
Documentation:
- Added
statsStatusfield and nullablestatsbehavior to health endpoint documentation - Added IETF rate-limit headers (
RateLimit,RateLimit-Policy) to rate limiting section - Added ETag conditional request examples in JavaScript and Python
- Clarified search behavior: contains matching means "lace" also matches "marketplace" and "placeholder"
- Added note about
totalItemsdiscrepancy between/directoryand/itemsendpoints (directory only includes categorized items)
v1.0.6 (2026-02-05)
Directory Endpoint:
- BREAKING: Renamed
linktowebsiteURLin directory item objects for consistency with the items endpoint
Validation:
- Fixed pagination accepting fractional page numbers (e.g.,
?page=1.5) -- now requires integers
CORS:
- Added
If-None-MatchtoAccess-Control-Allow-Headersacross all endpoints, enabling browser-based conditional requests - Added
Access-Control-Expose-Headers: ETagto directory and taxonomies responses, allowing browsers to read ETag values
Health Endpoint:
- Added structured request logging consistent with all other endpoints
Documentation:
- Fixed search description from "prefix matching" to "contains matching"
- Fixed search minimum from "3-100 chars" to "2-100 chars"
- Added
createdAtto sort options and item fields table - Added
firstPageandlastPageto metadata fields table and all example responses - Fixed pagination URLs in examples to show absolute URLs (matching actual API behavior)
- Fixed taxonomy sort metadata from
"priority"to"curated"in documentation - Added conditional requests (ETag) section to caching documentation
v1.0.5 (2026-02-04)
Directory Endpoint:
- BREAKING: Renamed
groupstosectionsin JSON response andtotalGroupstototalSectionsin metadata, for consistency with the rest of the API
Sort Metadata (all endpoints):
- BREAKING: Taxonomies endpoint
sortchanged from string ("alphabetical") to object ({field: "name", order: "asc"}) matching items endpoint format
Documentation:
- Added preview notice indicating API is not yet officially launched
v1.0.4 (2026-02-04)
Health Endpoint:
rateLimitfield restructured to show both data endpoint (100 req/min) and health endpoint (5,000 req/min) limits separately
Single Item Endpoint:
- Added
metadatafield withgeneratedtimestamp toGET /api/v1/items/[slug]response for consistency with other endpoints
Items Endpoint:
metadata.filtersnow always present (empty object{}when no filters applied)
Error Codes:
- Renamed
unknown_codeerror code tounknown_programming_languagefor clarity
Documentation:
- Corrected search description from "substring matching" to "prefix matching"
- Updated versioning statement to reflect early API history
- Sanitized internal error messages in directory endpoint
Taxonomy Counts:
- Aligned taxonomy
itemCountfilters with full item filter (added slug pattern, name pattern, and publishDate checks)
v1.0.3 (2026-02-04)
Pagination:
- Added
nextPageandpreviousPageURL strings to items metadata (null when not applicable)
Bug Fixes:
- Fixed taxonomy validation error responses returning malformed
{"0": {...}}instead of{errors: [...]}
v1.0.2 (2025-12-26)
Item Response Schema:
- BREAKING: Removed
tags[]array from item responses - Added
siteLanguagefield - single object with name, slug, miniDescription, adastackURL (or null) - Added
repoPrimaryLangfield - single object with name, slug, miniDescription, adastackURL (or null)
Filter Parameters:
- BREAKING: Removed
?tag=filter parameter - Added
?siteLanguage=parameter - filter by site language slug - Added
?programmingLanguage=parameter - filter by programming language slug
Taxonomies Endpoint:
- BREAKING: Replaced
tags[]withlanguages[]andprogrammingLanguages[]arrays - BREAKING: Replaced
totalTagswithtotalLanguagesandtotalProgrammingLanguagesin metadata
v1.0.1 (2025-01-20)
- BREAKING: Renamed
iconUrltoiconURL,imageUrltoimageURL - Added server-side image transform parameters
v1.0.0 (2025-01-15)
- Initial release with
/items,/items/[slug],/taxonomiesendpoints - Rate limiting (100 req/min), caching (1hr fresh, 2hr SWR), public CORS access
Versioning: Early v1 releases (v1.0.1, v1.0.2) included breaking changes as the API stabilized. Going forward, breaking changes will ship in new API versions (v2, v3). v1 is now stable.
Support: Email contact@adastack.io to report issues, request features, or discuss rate limit increases.