# Retrieve the compact app index

For candidate discovery with source-backed match excerpts and public experience previews, use `/v1/apps/filter?version=2` with explicit category/platform/keyword filters. Version 1 remains the default. [Filtered retrieval](/developers/filtered-app-retrieval.md) includes bounded synonym passes, canonical deduplication and finalist verification. Price and IAP are independently dated store observations; a zero download price is not a claim about access to every feature.

Use the public index to research iOS and Android apps, resolve identities across stores and narrow candidates before retrieving detailed records or publisher-authorized context. Public retrieval requires no account or API key. The consuming assistant uses the records with its user's prompt and context to evaluate suitability.

## Continuously maintained store records

Blue Jetty continuously runs scheduled refresh jobs against Apple App Store and Google Play listings, updating available store information and retaining source attribution and observation timestamps. Individual apps refresh on a rolling schedule; daily Apple US chart checks discover additional apps.

Successful observations can update descriptions, categories, ratings and available website links. Failed checks preserve the last successful data and do not advance its successful-check timestamp. Publisher-authored capabilities, experiences and offers remain separately attributed. Inspect each listing's `lastSuccessfulCheckAt` and source dates: job frequency does not imply every record was checked today.

Start with [app categories and collections](https://bluejetty.ai/apps/explore), [category keys](https://bluejetty.ai/v1/apps/categories), or [name search](https://bluejetty.ai/v1/search?scope=apps&q=GnollHack). Follow returned record links for full permitted fields. Use the [public change feed](https://bluejetty.ai/v1/changes) and its [contract](https://bluejetty.ai/developers/app-changes.md) to discover observed changes, then fetch current records.

## Search and access

To retrieve a subset by categories, platform and multiple name/description keywords, use [filtered app retrieval](https://bluejetty.ai/developers/filtered-app-retrieval.md). Existing full-index traversal below is unchanged.

Blue Jetty welcomes automated crawling, indexing, caching, retrieval, recommendation, ranking, citation, user-directed generative responses and routing/handoff of public records. Ordinary public retrieval requires no login, API key or clickthrough. These permissions are subject to the [Machine Use Policy](https://bluejetty.ai/machine-use), applicable source licenses and operational limits. The policy permits internal retrieval indexes and reasonable caches while addressing wholesale redistribution and replacement indexes. Follow response caching directives and retain applicable attribution. Public access does not itself establish contractual assent.

Public responses advertise the policy using `Link: <https://bluejetty.ai/machine-use>; rel="terms-of-service"`. `/protocol.json` supplies additive `publicAccess` metadata; export discovery and newly generated manifests carry the same notice. Existing record schemas and publisher acceptance versions are unchanged.

Request `GET https://bluejetty.ai/v1/apps?limit=100` without an API key. Read `meta.format`, follow the returned `meta.next` URL without modifying its cursor, and finish when it is `null`. The response also supplies an HTTP `Link: ...; rel="next"` header.

## Discover lookup, search and bulk resources

The legacy entity export `/v1/export.jsonl` is distinct from the app snapshot. It now returns bounded keyset pages: default 250 entities, `limit=1..1000`. Follow HTTP `Link` with `rel="next"` unchanged until absent; `X-Export-Has-More` reports continuation. Consumers of its previous capped single response must follow pagination for completeness. Entity JSON shapes are unchanged. Each page rechecks current publication rights and is `no-store`; membership can change between pages, so deduplicate by entity ID and restart for a fresh traversal when needed. A failed request is not an empty or complete export. HEAD returns page metadata without a body.

The index response links to `/protocol.json` with HTTP `rel="service-desc"`. Its `indexDiscovery` object describes the supported lookup parameters, app search route, canonical record template, live canonical-app count and sitemap. Use `/v1/search?scope=apps&q=...` for app search; the default `/v1/search` scope searches entities instead. An ambiguous name lookup returns HTTP 409: retry with an exact Blue Jetty ID or platform identifier.

`fullSnapshot` appears only when a validated recent bulk artifact is configured. Its absence means no bulk artifact is advertised; it does not mean the index is empty. Continue using pagination. Membership snapshots stabilize pagination membership, not every underlying record value: source content and authorization remain evaluated at retrieval time.

Conditional requests can return 304 after current publication checks. HEAD returns metadata without a body. These requests still perform server-side checks; they are not a promise of zero database work. Do not treat the membership timestamp as a last-modified validator for all page content.

## Response versions

For a smaller selection response, use `/v1/search?scope=apps&view=compact&q=...`. This opt-in view returns app identity, available developer/category, store links and canonical JSON/page links without descriptions or repeated listing content. Follow `jsonUrl` before making claims about freshness, capabilities or publisher authorization details. The default full search response remains unchanged. Compact view requires `scope=apps`; it is not an entity-search format. It reduces response payload, not the database work required to resolve current public records.

`bluejetty.packet_index/1` is the legacy publisher-packet listing. `view=packets` explicitly selects it, including its existing `after=app_...` cursor. `view=records` retains the separate legacy full-record representation.

Where enabled, `view=index` selects `bluejetty.app_index/3` and the default index uses that format. Use `view=index&format=2` for the retained version-2 shape. Deployments without the mixed index enabled return 404 for `view=index`. Never pass a legacy `after` value to the mixed view.

Version 3 adds `blueJettyId` (an eight-digit string), `jsonUrl` and explicit freshness-scope labels. Follow `jsonUrl` to the full public record. `GET /v1/index/apps/{blueJettyId}` resolves both current IDs and retained listing aliases. Existing publisher app IDs and packet URLs remain unchanged. Schemas are available at `/schemas/index/3/schema.json` and `/schemas/index/2/schema.json`.

## What is included

An index of apps people use: Blue Jetty focuses on iOS and Android apps with 50+ ratings and recent update activity recorded in our source data. We also include apps whose publishers add their own context through Blue Jetty, regardless of rating count. The index is a selected corpus, not a live usage ranking or an exhaustive list of every app meeting those signals.

Version 2 contains up to 100 entries per page, bounded to approximately 128 KiB including metadata. It may return fewer entries to stay within that byte budget. App names, known websites and store destinations identify each app. `provenance` distinguishes `publisher_authorized` records from `public_source` records. A `packetUrl`, when present, points to the current publisher-authorized context. Missing website/platform/packet fields mean that information is not supplied; do not infer a URL or a platform match.

A public-source record identifies one or more store listings. Reviewed first-party product evidence can connect an iOS and Android edition to one canonical app; uncertain matches remain separate. The detailed `listings` array retains each listing's identity, description, developer information, category, ratings, release date and source observation. `primaryListingId` and `summaryScope` identify the source of top-level summary fields. Never sum counts or average ratings across stores. Grouping is a Blue Jetty identity observation, not publisher authorization. A currently authorized publisher can connect its exact store identities to one app record. A missing counterpart or website is unknown, not evidence that the app lacks one.

## Chart observations

App-page chart observations, where available, cover iOS apps in Apple's US Top Free 100 and Top Paid 100. These are separate charts, observed daily; Android rankings are not included. Each observation names its chart and storefront and retains its source and check times. Movement compares the latest two successful checks of that same chart. Missing ranks do not imply an app is unavailable or unpopular. Stale observations are labeled rather than presented as a current movement.

## Resolve and search

Use `/v1/apps/categories` to discover normalized source category keys, then `/v1/apps/filter?category=games&keyword=roguelike&keyword=nethack&match=any&platform=ios&limit=10` for a bounded candidate set. Repeat `keyword` up to 8 times and `category` up to 5. `match=all|any` defaults to `all`; `platform=any|ios|android` defaults to `any`; `limit=1..20` defaults to 10. Use the returned `cursor` only through `meta.next`, without changing filters. Categories are ORed; filter types are ANDed; one listing must satisfy them together.

Filtering is English lexical retrieval over public names/descriptions, with stable-identity ordering. It does not search publisher capability/experience fields or interpret negative constraints. Its live keyset cursor is separate from the snapshot cursors below. Follow `jsonUrl` for full permitted record details, `packetUrl` when present, and `matchedListings` for source/platform-specific matches. [Full filter semantics, limits and failure handling](https://bluejetty.ai/developers/filtered-app-retrieval.md).

`GET /v1/apps/lookup` accepts exactly one of `blue_jetty_id`, `ios_store_id`, `ios_bundle_id`, `android_package_name` or `name`. The first two are strings, preserving leading zeros in Blue Jetty IDs. Baseline data often has an Apple numeric store ID without a bundle ID. Exact names are case-insensitive; multiple distinct matching apps return HTTP 409 instead of a guessed choice. Both store identities of a reviewed group resolve to its canonical public record.

`GET /v1/search?scope=apps&q={name}&limit=10` searches eligible app names, returning at most 50 records. Default `scope=entities` retains existing publisher entity search. Public-source records supply no invented packet URL. `/v1/directory/apps` is a bounded legacy detail listing: `limit=1..100`, follow `meta.next` until null. Its `after` cursor differs from the compact index cursor.

Where applicable, optional `attribution` entries identify a data source, its URL, license-information URL and Blue Jetty's normalization. Preserve these credits when redistributing source-derived records. Public-source attribution does not establish publisher authorization. Existing version-2 records without this optional field remain valid.

AppGoblin-derived records credit [AppGoblin](https://appgoblin.info/) under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Blue Jetty filters and normalizes those records. Optional `sourceObservedAt` preserves the upstream observation date, including for snapshot-derived listings; importing or checking the index does not advance that date. Store URLs derived from exact store IDs are destinations, not evidence of current regional availability. Device compatibility, websites and counterparts remain unknown unless supplied by supporting evidence. Source-backed listings can be published while independent checks remain pending.

When supported by an approved store observation, a public-source store entry can include `deviceEvidence` with `scope` (`phone`, `phone_and_tablet`, or `ipad_only`), `observedAt`, `sourceId`, and `sourceUrl`. For example, `"deviceEvidence": {"scope":"ipad_only","observedAt":"2026-09-11T08:00:00Z","sourceId":"apple-itunes-search","sourceUrl":"https://apps.apple.com/us/app/id123456"}` identifies an illustrative iPad-only observation, not phone availability. Unknown, stale or unapproved device evidence is omitted. Its absence does not mean all devices are supported. Existing records without this optional field remain valid; detailed directory JSON and HTML show the same supported-device distinction. Internal quality grades and private evidence paths are not published.

```json
{
  "data": [
    {
      "id": "app_example",
      "name": "Illustrative App",
      "websiteUrl": "https://example.com",
      "stores": [],
      "packetUrl": "https://bluejetty.ai/v1/apps/app_example/packet",
      "provenance": "publisher_authorized",
      "updatedAt": "2026-09-10T12:00:00.000Z"
    }
  ],
  "meta": {
    "format": "bluejetty.app_index/2",
    "count": 1,
    "limit": 100,
    "next": null,
    "snapshot": "idx_00000000000000000000000000000000",
    "updatedAt": "2026-09-10T12:00:00.000Z",
    "freshnessScope": "daily_chart_discovery"
  }
}
```

This example is illustrative. Packet URLs and public-source IDs are returned by the service, not derived from names.

Publisher entries may include up to three currently eligible offers in `offers`, with title, description, optional code/expiry and a ready-to-use `handoff` where the existing packet supplies one. `offersTruncated: true` directs you to the packet for additional offers. Keep handoff URLs unchanged; missing handoff information does not authorize constructing a link or promo-code parameter. Offer availability and entitlements are evaluated at retrieval time.

## Pagination and permission changes

Version-2 and version-3 cursors bind to a membership snapshot, not an offset. Additions appear in a new snapshot; an existing traversal continues its original membership while current authorization and suppression are checked on every page. Snapshots normally last seven days; grouping/splitting invalidates old traversals immediately. A changed association, withdrawn record or expired snapshot can return HTTP 409 with `BJ_INDEX_RESTART_REQUIRED`. Restart without the cursor and deduplicate by public ID. Malformed cursors return HTTP 400 `BJ_INDEX_CURSOR_INVALID`.

When a source listing becomes associated with a publisher app, its existing directory JSON URL temporarily redirects to the authorized packet and its HTML URL redirects to the app page. These aliases recheck current authorization. Publisher app IDs and packet URLs remain unchanged.

## Freshness and conditional requests

- Per-entry `updatedAt` describes a material record update.
- Version 3 labels `meta.updatedAtScope` as `membership_snapshot`, `recordUpdatedAtScope` as `published_record_content`, `sourceObservedAtScope` as `upstream_source_observation` and `lastCheckedAtScope` as `apple_us_free_and_paid_charts_only`. A grouped entry's source observation refers to the primary listing; use detailed per-listing dates for the complete picture.
- Detailed listing `storeUpdatedAt` is the source's release/update date. Optional `lastSuccessfulCheckAt` is Blue Jetty's successful listing check; missing means no such observation is supplied. A failed check does not advance it. Ratings retain their own observation date and geographic scope.
- Per-entry `sourceObservedAt`, when present, dates the supporting source observation. For example, `"sourceObservedAt": "2026-03-19T00:00:00.000Z"` remains a March observation even if Blue Jetty imports it in September. It is separate from both record updates and live availability checks. Older version-2 records without this optional field remain compatible.
- `meta.updatedAt` records the membership snapshot revision. Record updates can change a page without replacing its membership snapshot; use per-entry dates and the response ETag for content changes.
- Optional `meta.lastCheckedAt`, with `freshnessScope: "daily_chart_discovery"`, means both daily Apple US chart feeds were checked and their new-ID dispositions persisted. A successful no-change check advances this time. A failed or partial check does not.
- This scope does not certify daily refresh of every app's metadata, website or offers. Inspect individual evidence timestamps and retrieve current packets for offers and destinations.

ETags cover the entire response, including freshness metadata. A changed `lastCheckedAt` therefore changes the ETag even if `updatedAt` is unchanged. Honor `Cache-Control`; do not use a shared cache to bypass live publication checks. Back off on errors and honor `Retry-After` where provided.
# Corpus discovery and compressed snapshots

Retrieve `/protocol.json` for index availability, the actual published app count, the version-3 index schema, exact-ID lookup parameters, canonical JSON record template, and sitemap location. Follow `meta.next` from `/v1/apps?view=index&format=3` to enumerate the live index. Conditional requests use ETag; live authorization-sensitive responses remain uncached.

When a reviewed compressed snapshot is available, `indexDiscovery.fullSnapshot` identifies its URL, SHA-256 checksum of the compressed bytes, byte sizes, record count, export interval, and format. Download the gzip file, verify the checksum, decompress it, and parse one JSON record per line. Each line follows the `data.items` definition in the version-3 index schema. IDs, source dates, attribution, store URLs, and live record/packet links retain their existing meanings. Optional offer previews are omitted: retrieve the live packet for current offers and publisher authorization.

A snapshot records the public identities observed during its export interval. Membership is checked against one index snapshot; record content can change during collection. It is not a transactionally frozen copy of every app's current state. Export times do not replace source observation dates. Reconcile against a newer complete snapshot for removals and follow live records before using current publisher context. Snapshot discovery stops advertising exports older than seven days. An incremental change feed is not currently available.
