Skip to main content
Adastack Logo

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/taxonomies

Endpoints

Endpoints by Use Case

Content

Reference


GET /api/v1/items

List directory items with filtering, search, and pagination.

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number (1-10,000)
limitinteger20Items per page (1-100)
sortstringnameSort field: name, updatedAt, or createdAt
orderstring-asc or desc (default: asc for name, desc for date sorts)
categorystring-Filter by category slug
sectionstring-Filter by section/group slug
siteLanguagestring-Filter by site language slug
programmingLanguagestring-Filter by programming language slug
collectionstring-Filter by collection slug
searchstring-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=lace matches "Lace wallet" AND "marketplace"
  • search=swap matches "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

FieldTypeDescription
namestringDisplay name (always present)
slugstringURL-safe identifier (always present)
createdAtstringISO-8601 timestamp of initial creation
categoryobject | null{name, slug, adastackURL}
sectionobject | null{name, slug, adastackURL}
miniDescriptionstring | nullOne-liner description
descriptionstring | nullFull description
introductionstring | nullIntro paragraph
websiteURLstring | nullMain website
blogURLstring | nullBlog URL
whitepaperURLstring | nullWhitepaper/docs URL
xURLstring | nullX (Twitter) profile
discordURLstring | nullDiscord invite
telegramURLstring | nullTelegram group/channel
linkedinURLstring | nullLinkedIn page
facebookURLstring | nullFacebook page
youtubeURLstring | nullYouTube channel
teamGithubURLstring | nullGitHub org/team URL
projectGithubURLstring | nullGitHub repo URL
repoPrimaryLangobject | null{name, slug, miniDescription, adastackURL}
siteLanguageobject | null{name, slug, miniDescription, adastackURL}
collectionsarray[{name, slug, miniDescription, adastackURL}] (may be empty)
iconURLstring | nullSquare logo (200x200, auto-optimized)
imageURLstring | nullHero image (1920x1080, auto-optimized)
adastackURLstringItem page URL (always present)

Metadata Fields

FieldTypeDescription
totalintegerTotal items matching filters
pageintegerCurrent page number
limitintegerItems per page
totalPagesintegerTotal pages available
hasMorebooleanWhether more pages exist
firstPagestring | nullURL for first page, or null when no results
lastPagestring | nullURL for last page, or null when no results
nextPagestring | nullURL for next page, or null on last page
previousPagestring | nullURL for previous page, or null on first page
sortobjectApplied sort {field, order}
filtersobjectApplied filters including search term (empty object {} when no filters applied)
generatedstringISO 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-wallet

Response 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/health

Response 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

FieldTypeDescription
statusstringAlways "ok" if the API is responding
statsStatusstring"ok" if stats loaded, "unavailable" if CMS query failed
statsobject | nullDirectory 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/taxonomies

Query Parameters

ParameterTypeDefaultDescription
sortstringalphabeticalalphabetical (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)

HeaderExampleDescription
RateLimitlimit=100, remaining=95, reset=45Current limit, remaining requests, seconds until reset
RateLimit-Policy100;w=60Limit and window size (60 seconds)

Legacy Headers (maintained for backward compatibility)

HeaderDescription
X-RateLimit-LimitMax requests per window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix 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:

EndpointCache-ControlETag
/items, /items/[slug]public, max-age=3600 (1 hour)No
/taxonomiespublic, max-age=86400 (24 hours)Yes
/healthno-storeNo

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 unchanged

CORS 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

CodeHTTPDescriptionResolution
invalid_query400Invalid query parametersCheck parameter types and ranges
invalid_slug400Malformed slug formatUse lowercase alphanumeric with hyphens
not_found404Resource doesn't existVerify slug spelling
unknown_category422Category slug not foundUse /api/v1/taxonomies for valid slugs
unknown_section422Section slug not foundUse /api/v1/taxonomies for valid slugs
unknown_language422Language slug not foundUse /api/v1/taxonomies for valid slugs
unknown_programming_language422Programming language slug not foundUse /api/v1/taxonomies for valid slugs
unknown_collection422Collection slug not foundUse /api/v1/taxonomies for valid slugs
rate_limit_exceeded429Too many requestsWait for Retry-After header value
query_failed500Database query failedRetry; contact support if persistent
internal_error500Server errorRetry; 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 updatedAt field from item responses (internal CMS metadata not appropriate for public API; sorting by updatedAt still 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 statsStatus field and nullable stats behavior 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 totalItems discrepancy between /directory and /items endpoints (directory only includes categorized items)

v1.0.6 (2026-02-05)

Directory Endpoint:

  • BREAKING: Renamed link to websiteURL in 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-Match to Access-Control-Allow-Headers across all endpoints, enabling browser-based conditional requests
  • Added Access-Control-Expose-Headers: ETag to 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 createdAt to sort options and item fields table
  • Added firstPage and lastPage to 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 groups to sections in JSON response and totalGroups to totalSections in metadata, for consistency with the rest of the API

Sort Metadata (all endpoints):

  • BREAKING: Taxonomies endpoint sort changed 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:

  • rateLimit field restructured to show both data endpoint (100 req/min) and health endpoint (5,000 req/min) limits separately

Single Item Endpoint:

  • Added metadata field with generated timestamp to GET /api/v1/items/[slug] response for consistency with other endpoints

Items Endpoint:

  • metadata.filters now always present (empty object {} when no filters applied)

Error Codes:

  • Renamed unknown_code error code to unknown_programming_language for 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 itemCount filters with full item filter (added slug pattern, name pattern, and publishDate checks)

v1.0.3 (2026-02-04)

Pagination:

  • Added nextPage and previousPage URL 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 siteLanguage field - single object with name, slug, miniDescription, adastackURL (or null)
  • Added repoPrimaryLang field - 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[] with languages[] and programmingLanguages[] arrays
  • BREAKING: Replaced totalTags with totalLanguages and totalProgrammingLanguages in metadata

v1.0.1 (2025-01-20)

  • BREAKING: Renamed iconUrl to iconURL, imageUrl to imageURL
  • Added server-side image transform parameters

v1.0.0 (2025-01-15)

  • Initial release with /items, /items/[slug], /taxonomies endpoints
  • 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.