openapi: 3.1.0
info:
  title: QuantFlow Public API
  version: 1.0.0
  summary: Open roles at QuantFlow, the Amsterdam quant and crypto recruitment consultancy.
  description: |-
    The QuantFlow public API exposes the roles QuantFlow is actively hiring for in
    quantitative trading, crypto, fintech and AI, across the Netherlands and Europe.

    No authentication and no API key. The same data is published on
    https://www.quant-flow.com/careers, so there is nothing to gate.

    Use it to answer "what is QuantFlow hiring right now", to match a candidate
    against open mandates, or to keep a job aggregator in sync. Many mandates stay
    confidential and never appear here, so an empty list does not mean an empty desk;
    for those, contact hello@quant-flow.com.

    Every page on the site is also available as markdown per acceptmarkdown.com:
    send `Accept: text/markdown` or append `.md` to any path.
  contact:
    name: QuantFlow
    email: hello@quant-flow.com
    url: https://www.quant-flow.com/developers
  license:
    name: Job data may be reproduced with attribution to QuantFlow.
    url: https://www.quant-flow.com/terms
servers:
  -
    url: https://www.quant-flow.com
    description: Production
externalDocs:
  description: QuantFlow developer documentation
  url: https://www.quant-flow.com/developers
tags:
  -
    name: Jobs
    description: Read the roles QuantFlow is currently hiring for.
  -
    name: Meta
    description: Endpoint discovery.
paths:
  "/api":
    get:
      operationId: listEndpoints
      tags:
        - Meta
      summary: List the published API endpoints
      description: Returns a machine-readable directory of every public endpoint, with a link to this OpenAPI document. Start here when discovering the API.
      responses:
        "200":
          description: The endpoint directory.
          content:
            "application/json":
              schema:
                type: object
                required:
                  - object
                  - openapi
                  - documentation
                  - endpoints
                properties:
                  object:
                    type: string
                    const: api_index
                  name:
                    type: string
                  version:
                    type: string
                  openapi:
                    type: string
                    format: uri
                  documentation:
                    type: string
                    format: uri
                  authentication:
                    type: string
                    description: How to authenticate. Currently "none".
                  endpoints:
                    type: array
                    items:
                      type: object
                      required:
                        - method
                        - path
                        - operationId
                        - description
                      properties:
                        method:
                          type: string
                        path:
                          type: string
                        operationId:
                          type: string
                        description:
                          type: string
  "/api/jobs":
    get:
      operationId: listJobs
      tags:
        - Jobs
      summary: List open roles
      description: Returns the roles QuantFlow is actively hiring for, newest first. Filter with `q` and `location`, page with `limit` and `offset`. Cached for five minutes.
      parameters:
        -
          name: q
          in: query
          required: false
          description: Case-insensitive substring match over title, description, location and seniority.
          schema:
            type: string
            maxLength: 200
          example: c++
        -
          name: location
          in: query
          required: false
          description: Case-insensitive substring match over the location field.
          schema:
            type: string
            maxLength: 100
          example: Amsterdam
        -
          name: limit
          in: query
          required: false
          description: Page size.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        -
          name: offset
          in: query
          required: false
          description: Number of roles to skip.
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        "200":
          description: A page of open roles.
          content:
            "application/json":
              schema:
                "$ref": "#/components/schemas/JobList"
        "400":
          description: A query parameter was out of range.
          content:
            "application/json":
              schema:
                "$ref": "#/components/schemas/Error"
        "502":
          description: The upstream job board was unreachable.
          content:
            "application/json":
              schema:
                "$ref": "#/components/schemas/Error"
  "/api/jobs/{slug}":
    get:
      operationId: getJob
      tags:
        - Jobs
      summary: Get one open role
      description: Returns a single role by slug. Slugs come from `listJobs`. Roles are removed once filled, so a slug that resolved before can return 404.
      parameters:
        -
          name: slug
          in: path
          required: true
          description: The role identifier, as returned by listJobs.
          schema:
            type: string
            maxLength: 200
          example: algorithmic-trader
      responses:
        "200":
          description: The role.
          content:
            "application/json":
              schema:
                "$ref": "#/components/schemas/Job"
        "400":
          description: The slug was missing or too long.
          content:
            "application/json":
              schema:
                "$ref": "#/components/schemas/Error"
        "404":
          description: No open role is published with that slug.
          content:
            "application/json":
              schema:
                "$ref": "#/components/schemas/Error"
        "502":
          description: The upstream job board was unreachable.
          content:
            "application/json":
              schema:
                "$ref": "#/components/schemas/Error"
components:
  schemas:
    Job:
      type: object
      description: An open role QuantFlow is hiring for.
      required:
        - slug
        - title
        - hiring_organization
        - url
        - apply_url
      properties:
        slug:
          type: string
          description: Stable identifier, and the last path segment of `url`.
          examples:
            - algorithmic-trader
        title:
          type: string
          description: Role title as advertised.
          examples:
            - Algorithmic Trader
        description:
          type:
            - string
            - "null"
          description: Plain-text role description. May contain newlines.
        location:
          type:
            - string
            - "null"
          description: Free-text location as advertised.
          examples:
            - Amsterdam, Netherlands
        work_arrangement:
          type:
            - string
            - "null"
          description: Working pattern, when stated.
          examples:
            - onsite
            - hybrid
            - remote
        employment_type:
          type:
            - string
            - "null"
          description: Contract type, when stated.
          examples:
            - FULL_TIME
        seniority:
          type:
            - string
            - "null"
          description: Seniority band, when stated.
        salary:
          type:
            - object
            - "null"
          description: Advertised salary range. Null when the client does not publish one, which is most senior trading roles.
          properties:
            min:
              type:
                - number
                - "null"
              description: Lower bound of the annual base salary.
            max:
              type:
                - number
                - "null"
              description: Upper bound of the annual base salary.
            currency:
              type:
                - string
                - "null"
              description: ISO 4217 code.
              examples:
                - EUR
        hiring_organization:
          type: string
          description: The hiring party. Client names stay confidential on most mandates, so this is usually "QuantFlow".
        date_posted:
          type:
            - string
            - "null"
          format: date-time
          description: ISO 8601 timestamp the role was published.
        valid_through:
          type:
            - string
            - "null"
          format: date-time
          description: ISO 8601 timestamp the posting expires, when the client set one.
        url:
          type: string
          format: uri
          description: Human-readable posting on quant-flow.com.
        apply_url:
          type: string
          format: uri
          description: Where a candidate applies.
    JobList:
      type: object
      description: A page of open roles.
      required:
        - object
        - total
        - limit
        - offset
        - data
      properties:
        object:
          type: string
          const: list
          description: Always "list".
        total:
          type: integer
          description: Number of roles matching the filters, before paging.
        limit:
          type: integer
          description: Page size that was applied.
        offset:
          type: integer
          description: Offset that was applied.
        data:
          type: array
          description: The roles on this page.
          items:
            "$ref": "#/components/schemas/Job"
    Error:
      type: object
      description: Every error under /api uses this shape.
      required:
        - code
        - error
        - message
        - hint
        - docs
      properties:
        code:
          type: string
          description: Stable machine-readable error code. Safe to branch on.
          examples:
            - job_not_found
            - invalid_query_parameter
            - rate_limited
        error:
          type: string
          description: Duplicate of `code`, kept for older clients.
        message:
          type: string
          description: One sentence a person can read.
        hint:
          type: string
          description: What the caller should do next.
        docs:
          type: string
          format: uri
          description: Link to the documentation.
        details:
          type: object
          description: Field-level detail, present on validation failures.
          additionalProperties:
            type: string
