{
  "openapi": "3.1.0",
  "info": {
    "title": "RemoShift Jobs API",
    "version": "1.0.0",
    "description": "Remote job data for AI assistants and integrations. Two endpoints: an OPEN read-only one (no key) and a TOKEN-GATED one that adds full descriptions. Job data refreshes hourly; cache your results. If you display these jobs, link back to `detail_url` and to https://remoshift.com.",
    "contact": {
      "url": "https://remoshift.com/about"
    },
    "license": {
      "name": "Attribution required",
      "url": "https://remoshift.com/llms.txt"
    }
  },
  "servers": [
    {
      "url": "https://remoshift.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Open",
      "description": "Free, no authentication. Link-first job summaries."
    },
    {
      "name": "Gated",
      "description": "Requires DATA_API_KEY. Adds full descriptions."
    }
  ],
  "paths": {
    "/api/v1/jobs.open": {
      "get": {
        "tags": [
          "Open"
        ],
        "operationId": "searchOpenJobs",
        "summary": "Search live remote jobs (no key required)",
        "description": "Read-only, Meili-backed, link-first summaries. Max 25 jobs per page. Rate limited to 60 requests/min/IP (HTTP 429 with Retry-After: 60). Responses are CDN-cached for 300s with stale-while-revalidate. detail_url is always present and is the canonical RemoShift page for the job - link back to it. apply_url is the employer's own posting or ATS page and is null when we do not have one, so a consumer must handle null and fall back to detail_url.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Full-text query. Truncated to 100 chars."
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 40
            },
            "description": "Filter by tag, lowercased."
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Filter by category slug."
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 10
            },
            "description": "ISO-3166 alpha-2 country code, uppercased (e.g. `US`, `PT`). Country NAMES are not supported."
          },
          {
            "name": "anywhere",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "Set to `1` to return only jobs with no country restriction."
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 40
            },
            "description": "Filter by source, e.g. `Himalayas`, `Greenhouse`, `Lever`."
          },
          {
            "name": "min_posted",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only jobs posted on or after this date, `YYYY-MM-DD`. Compared on epoch seconds internally; a malformed value is ignored rather than erroring."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 1
            },
            "description": "1-based page number."
          },
          {
            "name": "per",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 15
            },
            "description": "Results per page. Hard maximum 25."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "oldest"
              ]
            },
            "description": "Omit for newest-first (default). `oldest` reverses it."
          }
        ],
        "responses": {
          "200": {
            "description": "Matching jobs",
            "headers": {
              "Link": {
                "schema": {
                  "type": "string"
                },
                "description": "`rel=\"describedby\"` pointing at /llms.txt"
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string"
                },
                "description": "`*` \u2014 callable from a browser or agent sandbox"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenJobsResponse"
                },
                "examples": {
                  "one": {
                    "summary": "One nurse role",
                    "value": {
                      "meta": {
                        "total": 1924,
                        "page": 1,
                        "per": 1,
                        "refreshed_hourly": true,
                        "attribution": "If you display these jobs, link back to the job url and to remoshift.com."
                      },
                      "jobs": [
                        {
                          "id": "67d5d6728ef08a94",
                          "title": "Remote Advanced Practice Provider, Nurse Practitioner, NP, Weekends, (part time)",
                          "company": "ChenMed",
                          "location": "United States",
                          "salary": "USD 111,140 - 158,771 / annual",
                          "source": "Himalayas",
                          "posted_at": "2026-08-23",
                          "work_anywhere": false,
                          "apply_url": "https://jobs.ashbyhq.com/socket/c1625d37-2c92-4455-8d3a-5ed3",
                          "detail_url": "https://remoshift.com/job/remote-advanced-practice-provider-nurse-practitioner-np-week-67d5d672"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (more than 60 requests/min from one IP)",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait (60)"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Search backend temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/jobs": {
      "get": {
        "tags": [
          "Gated"
        ],
        "operationId": "searchJobsFull",
        "summary": "Search live remote jobs with full descriptions",
        "description": "Same filters as the open endpoint, but returns the full `description` (and allows up to 200 per page) using limit/offset instead of page/per. Requires a token: send `Authorization: Bearer <DATA_API_KEY>`, or `?key=<DATA_API_KEY>`, or `?api_key=`, or `?bearer=`.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyQuery": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Full-text query."
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "ISO-3166 alpha-2 code."
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "anywhere",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_posted",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching jobs with full descriptions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatedJobsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer <DATA_API_KEY>`"
      },
      "apiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "`?key=<DATA_API_KEY>` (`api_key` and `bearer` are accepted aliases)"
      }
    },
    "schemas": {
      "OpenJobsResponse": {
        "type": "object",
        "required": [
          "meta",
          "jobs"
        ],
        "properties": {
          "meta": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer",
                "description": "Total matches for the query. `0` means no match \u2014 do not present semantic suggestions as matches."
              },
              "page": {
                "type": "integer"
              },
              "per": {
                "type": "integer"
              },
              "refreshed_hourly": {
                "type": "boolean"
              },
              "attribution": {
                "type": "string"
              }
            }
          },
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpenJob"
            }
          }
        }
      },
      "OpenJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "16 lowercase hex chars. The last 8 appear at the end of the canonical URL."
          },
          "title": {
            "type": "string"
          },
          "company": {
            "type": "string"
          },
          "location": {
            "type": "string"
          },
          "salary": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string"
          },
          "posted_at": {
            "type": "string",
            "description": "`YYYY-MM-DD`. May be empty when the source does not publish a date."
          },
          "work_anywhere": {
            "type": "boolean",
            "description": "True only when the role has no country restriction. Mutually exclusive with a non-empty `countries` filter."
          },
          "apply_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The employer's own posting or ATS page. NULL when we have no employer link (81% of jobs come from boards that host the posting themselves) - then use detail_url."
          },
          "detail_url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical RemoShift page - always present. Link back to this; it is the route for every job, including those with no apply_url."
          }
        }
      },
      "GatedJobsResponse": {
        "type": "object",
        "required": [
          "meta",
          "jobs"
        ],
        "properties": {
          "meta": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "limit": {
                "type": "integer"
              },
              "offset": {
                "type": "integer"
              },
              "source": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "tag": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "q": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "jobs": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/OpenJob"
                },
                {
                  "type": "object",
                  "properties": {
                    "category": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "description": {
                      "type": "string",
                      "description": "Full job description, HTML-stripped text."
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    }
  },
  "externalDocs": {
    "url": "https://remoshift.com/llms.txt",
    "description": "Agent guide: how to use and cite this site"
  }
}