jobopportunitiesapi-org Meta API

The meta API from jobopportunitiesapi-org — 6 operation(s) for meta.

Operations 6

GET /v1/me Your key, plan and quota #
GET /v1/me/export-estimate Approximately how many rows a search would return, and whether the CSV export… #
GET /v1/me/usage Requests served per day, for the dashboard's usage chart #
GET /v1/meta/facets Every filter value with its live count #
GET /v1/meta/freshness How recently the ledger was verified, and how much of it is inferred #
GET /v1/meta/providers Every source the ledger is built from, with its live row count #

Work with this as data

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/jobopportunitiesapi-org:jobopportunitiesapi-org-meta-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

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 Specification

jobopportunitiesapi-org-meta-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  description: 'Every listing here comes from an employer ATS, a company''s own careers page,

    a public employment agency, or a direct submission. Redistribution is

    enforced by a database JOIN, not a filter someone can forget.


    Three things this API does that a job feed normally does not:


    * **It says which fields it guessed.** Every job carries `field_sources`,

    naming each field as `published` (carried by the source), `inferred`

    (derived by us) or `absent`. 88.0% of live listings have an *inferred*

    remote status; 11.3% have one the source actually stated. Filter with

    `remote_confirmed=true` to get only the latter.

    * **It says when each row was last verified.** `last_verified_at` is when we

    last confirmed the vacancy still exists at its source, not when it was

    posted. `GET /v1/meta/freshness` publishes the coverage numbers.

    * **It keeps what closed.** `GET /v1/jobs/closed` returns roles that have

    come off their source, with the reason and the date. A req that opened

    and closed in nine days is a hiring signal, not a dead link. The date is

    the day JOA''s daily verification confirmed the advert had gone; before

    late September 2026 that verification ran in large catch-up batches, so

    earlier closure dates show when JOA confirmed a closure, not when the

    employer closed the role.


    Changelog 2026-10-07: new contract fields `engagement`,

    `contract_category`, `agency` and `working_schedule`, each with

    `field_sources` provenance and filters (`?engagement=`,

    `?contract_category=`, `?agency=`, `?working_schedule=`; absence token

    `unknown`, alias `not_stated`). Values appear only when the posting or the

    source states them (or, later, when an audited rule establishes them) —

    most postings will show `unknown` at launch, and that is the honest answer,

    not a defect. `contract_category=permanent` returns only postings that

    STATE permanence. Per-field coverage is published on /public/coverage.

    `employment_type` is unchanged and frozen.


    Counts in this document are not typed in: they come from the daily

    statistics snapshot every page of the website uses (GET /public/site-stats):

    2,080,732 live employer-direct listings (employer_direct_live) and

    129,261 employers as of 2026-10-20.'
  summary: A verified ledger of open jobs, with per-field provenance and closure history.
  title: Job Opportunities Meta API
  version: '2026-10-07'
servers:
- url: https://api.jobopportunitiesapi.org
security:
- bearerAuth: []
tags:
- name: Meta
paths:
  /v1/me:
    get:
      description: 'Costs no records. `usage_today` counts REQUESTS today (the per_day burst limit); the billed unit is records: `records_used_month` of `records_limit_month` (null = unmetered key), resetting at `records_reset_at` (00:00 UTC on the 1st). With a country lock (`records_country_lock`) job rows in that country are not counted; company rows still are.'
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  created:
                    type:
                    - string
                    - 'null'
                  key_id:
                    format: uuid
                    type: string
                  key_prefix:
                    type: string
                  limits:
                    properties:
                      max_page_size:
                        type: integer
                      per_day:
                        type: integer
                      per_minute:
                        type: integer
                      ranked_per_day:
                        type: integer
                    type: object
                  plan:
                    type: string
                  records_country_lock:
                    description: ISO country of a country-locked key (job rows there are not metered).
                    type:
                    - string
                    - 'null'
                  records_limit_month:
                    description: Monthly record allowance; null for an unmetered key.
                    type:
                    - integer
                    - 'null'
                  records_reset_at:
                    description: When the record allowance resets (1st of next month 00:00 UTC).
                    format: date-time
                    type: string
                  records_used_month:
                    description: Records charged this calendar month (UTC).
                    type: integer
                  renews_or_expires:
                    type:
                    - string
                    - 'null'
                  status:
                    type: string
                  usage_today:
                    description: Requests today (not records).
                    type: integer
                type: object
          description: Key status.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
      summary: Your key, plan and quota
      tags:
      - Meta
      operationId: getV1Me
      x-operation-id-source: derived
  /v1/me/export-estimate:
    get:
      description: 'Accepts the same filters as GET /v1/jobs. Free to call: it never returns rows and consumes no record quota. estimated_rows is the query planner''s approximation (EXPLAIN, no execution), not an exact count.'
      responses:
        '200':
          description: estimated_rows, export_cap, would_use, remaining_this_month.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/RateLimited'
      summary: Approximately how many rows a search would return, and whether the CSV export…
      tags:
      - Meta
      operationId: getV1MeExportEstimate
      x-operation-id-source: derived
  /v1/me/usage:
    get:
      parameters:
      - description: Only requests is available today.
        in: query
        name: metric
        schema:
          enum:
          - requests
          type: string
      - description: Window size. Defaults to 30.
        in: query
        name: days
        schema:
          enum:
          - 30
          - 90
          type: integer
      responses:
        '200':
          description: Daily request counts, oldest first.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/RateLimited'
      summary: Requests served per day, for the dashboard's usage chart
      tags:
      - Meta
      operationId: getV1MeUsage
      x-operation-id-source: derived
  /v1/meta/facets:
    get:
      description: 'Facet keys: `family`, `country`, `city`, `remote`, `employment`,

        `seniority`, `source_type`, `remote_source`, `provider`, `field`, and

        the legacy `source`.


        `remote_source` counts the provenance split itself — how much of the

        ledger''s remote status is stated and how much is our inference.

        `provider` is every source name `?provider=` accepts, with its live row

        count. `field` is how many live rows carry each field as `published`

        rather than inferred — the population `require_fields` selects from.'
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    additionalProperties:
                      items:
                        properties:
                          count:
                            type: integer
                          label:
                            type: string
                          value:
                            type: string
                        type: object
                      type: array
                    type: object
                  generated_at:
                    description: 'When these counts were computed. The whole table is

                      rebuilt in one transaction, so this describes all of it.

                      Without it, a facet total that differs from

                      /public/coverage by a few thousand rows is

                      indistinguishable from a real disagreement — the ledger

                      moves by thousands an hour.

                      '
                    format: date-time
                    type: string
                type: object
          description: Facet counts.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
      summary: Every filter value with its live count
      tags:
      - Meta
      operationId: getV1MetaFacets
      x-operation-id-source: derived
  /v1/meta/freshness:
    get:
      description: 'The claim the subscription is sold on, made checkable. Every live

        listing is re-confirmed at its source on a rolling cycle; this reports

        the coverage, plus per-field provenance totals so a buyer can size the

        honest subset before they pay for it.'
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    properties:
                      closures:
                        properties:
                          last_24h:
                            type: integer
                          last_7d:
                            type: integer
                        type: object
                      field_coverage:
                        properties:
                          description:
                            type: integer
                          employment_type:
                            type: integer
                          remote_inferred:
                            type: integer
                          remote_inferred_pct:
                            type: number
                          remote_published:
                            type: integer
                          remote_published_pct:
                            type: number
                          salary_published:
                            type: integer
                        type: object
                      generated_at:
                        type: string
                      live_listings:
                        type: integer
                      verification:
                        properties:
                          newest_verification:
                            type: string
                          oldest_verification:
                            type: string
                          reverified_last_24h:
                            type: integer
                          reverified_last_24h_pct:
                            type: number
                          reverified_last_48h:
                            type: integer
                          reverified_last_48h_pct:
                            type: number
                          reverified_last_7d:
                            type: integer
                          reverified_last_hour:
                            type: integer
                        type: object
                    type: object
                type: object
          description: Freshness and coverage report.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
      summary: How recently the ledger was verified, and how much of it is inferred
      tags:
      - Meta
      operationId: getV1MetaFreshness
      x-operation-id-source: derived
  /v1/meta/providers:
    get:
      description: 'The list `?provider=` and `?exclude_provider=` accept. A value that is

        not here is a 422, never a silently empty page — you can always tell

        "no jobs match" from "you spelled it wrong".


        Only redistributable sources appear. Sources JOA reads but does not

        republish are named at `GET /public/coverage` (`excluded_sources`).


        Also served without a key at `GET /public/providers`, because "which

        sources is this built from" is a question you should be able to answer

        before creating an account.'
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    items:
                      properties:
                        label:
                          type: string
                        live_listings:
                          type: integer
                        provider:
                          description: The value to pass to ?provider=.
                          type: string
                        source_class:
                          enum:
                          - employer_ats
                          - government
                          - direct
                          type: string
                        source_type:
                          type: string
                        with_description:
                          description: 'How many of this source''s live rows carry the

                            employer''s advert text. Published including the

                            sources where it is zero — three of them have no

                            adapter in the description crawl at all, so they

                            contribute rows and no text, and

                            `?provider=<that>&require_fields=description` returns

                            an empty page. Knowing that here beats discovering it

                            mid-evaluation.

                            '
                          type: integer
                        with_description_pct:
                          type: number
                      type: object
                    type: array
                  generated_at:
                    format: date-time
                    type: string
                  note:
                    type: string
                type: object
          description: The provider list.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
      summary: Every source the ledger is built from, with its live row count
      tags:
      - Meta
      operationId: getV1MetaProviders
      x-operation-id-source: derived
components:
  responses:
    Unprocessable:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: 'A parameter we will not guess at: an unknown source_type or provider, an unparseable timestamp, a malformed cursor. Note the deliberate exceptions, which are clamps rather than errors: `limit` is capped at the plan maximum (a non-numeric value falls back to the default), and comma-separated lists are truncated to the per-parameter maximum shown on each. Unknown query parameters are ignored.

        '
    Unauthorized:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Missing, unknown, revoked or expired key. All four are reported identically.
    RateLimited:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/LimitError'
      description: 'Over your plan''s per-minute or per-day limit. `Retry-After` says how long (seconds to the next minute boundary, or to UTC midnight on a daily breach). The body says exactly when the limit resets (`resets_at`, `resets_at_text`) and, when a higher plan exists, links a same-key upgrade (`upgrade_url`).

        '
      headers:
        Retry-After:
          description: Seconds until the limit resets.
          schema:
            type: integer
        X-RateLimit-Reset:
          description: Unix seconds of the next UTC midnight, when the daily counter resets.
          schema:
            type: integer
  schemas:
    Error:
      properties:
        docs:
          type: string
        error:
          type: string
        message:
          type: string
      type: object
    LimitError:
      allOf:
      - $ref: '#/components/schemas/Error'
      - properties:
          docs_url:
            description: The documentation section on this limit.
            format: uri
            type: string
          limit:
            description: The limit that was hit, in the unit of `window` (records per month, requests per day or per minute).
            format: int64
            type: integer
          plan:
            description: The key's plan, e.g. `explore`.
            type: string
          resets_at:
            description: The exact reset instant, RFC 3339, always UTC.
            example: '2026-11-01T00:00:00Z'
            format: date-time
            type: string
          resets_at_text:
            description: The same instant in plain English, UTC.
            example: 1 November 2026 at 00:00 UTC
            type: string
          upgrade_plan:
            description: The plan `upgrade_url` moves the key to. `growth` for a free (Explore) key or a live trial (the one-click link); otherwise the next plan up. Absent for the top plan and for marketplace keys.
            example: growth
            type: string
          upgrade_url:
            description: 'For a free (Explore) key or a live trial that hit the monthly record limit: a one-click link for the SAME key

              (`https://jobopportunitiesapi.org/upgrade?k=<signed token>`). Opening it, no sign-in needed, redirects to a

              personal Creem checkout that upgrades this key to Growth monthly; same secret, nothing to change in the integration.

              The token is signed, scoped to the key and valid for 48 hours. For any other key it is a same-key checkout link for

              `upgrade_plan` (the signed-in owner of the key completes it).

              '
            format: uri
            type: string
          used:
            description: Usage in that window when the limit was hit.
            format: int64
            type: integer
          window:
            description: Which limit was hit. `calendar_month_utc` resets on the 1st of the next month at 00:00 UTC; `day_utc` at the next 00:00 UTC; `minute` at the next minute boundary.
            enum:
            - calendar_month_utc
            - day_utc
            - minute
            type: string
        type: object
      description: 'The body of a 402 `record_quota_exhausted` and a 429 `rate_limited`. `error`, `message` and `docs` are the same envelope as every other error; the remaining fields are optional additions. Marketplace channel keys and keys with no higher plan get the reset time but no `upgrade_*` fields.

        '
  securitySchemes:
    bearerAuth:
      description: '`Authorization: Bearer YOUR_API_SECRET`'
      scheme: bearer
      type: http