Frase Research API

AI-powered research

GitHub OpenAPI

Operations 5

GET /research List research #
POST /research Start research #
GET /research/{id} Get research #
DELETE /research/{id} Delete research #
GET /research/{id}/export Export research #

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

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

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

frase-io-research-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Frase Research API
  version: 1.0.0
  description: 'The Frase API provides programmatic access to your SEO content platform. Use it to manage content, research, audits, AI visibility tracking, and more. ## Authentication


    All API requests require authentication using an API key. Include your key in the `X-API-KEY` header:


    ```

    X-API-KEY: sk_live_your_api_key_here

    ```


    For legacy compatibility, the `token` header is also supported. ## Rate Limits


    Three independent limits apply, each with its own headers and error code. Headers are returned on `429` responses, not on successful ones. **Per-minute burst** - 60/min on trial and Starter, 120 Professional, 300 Scale, 1,000 Enterprise. Error code `rate_limit_exceeded`. - `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (epoch seconds)


    **Sustained hourly ceiling** - 2,000/hour trial, 3,000 Starter, 5,000 Professional, 15,000 Scale, 50,000 Enterprise. Error code `hourly_limit_exceeded`. - `X-RateLimit-Hourly-Limit`, `X-RateLimit-Hourly-Remaining`, `X-RateLimit-Hourly-Reset` (epoch seconds)


    **Monthly request budget** - error code `monthly_budget_exceeded`, headers `X-Monthly-Limit`, `X-Monthly-Used`, `X-Monthly-Remaining`. Every `429` carries `Retry-After` in seconds. On the hourly ceiling this can be up to 3,600, so clients should honour it rather than retrying on a short fixed backoff - a retry sooner than `Retry-After` cannot succeed. ## Errors


    The API uses standard HTTP status codes and returns errors in a consistent format:


    ```json

    {

    "error": {

    "type": "authentication_error",

    "code": "invalid_api_key",

    "message": "Invalid API key provided.",

    "doc_url": "https://docs.frase.io/api/errors#invalid_api_key"

    }

    }

    ```


    ## Changelog


    Breaking changes are listed here, newest first. "Breaking" means a field that was always present with a value can now be `null`, or an existing value now means something different — not that a field was removed or renamed. Additive fields are not listed. ### 2026-08-31 — Fact `sources[]` may omit `url`, and minted facts default to `approved`


    - `GET /briefs/{id}/facts`, `PATCH /briefs/{id}/facts/{factId}` → a `sources[]` entry may now carry only `title`, with no `url` or `domain`. Research names a publisher far more often than it captures a link (793 of 801 plan-evidence items in production), and those names were previously discarded rather than returned. Read `url` defensively. - `GET /briefs/{id}/facts` → `approval` on a newly minted fact is now `approved` rather than `pending`. A client polling for `pending` facts to review will see far fewer; rejection is unchanged and remains permanent. Facts the writer asserted in its own output (`provenance: generated`) are still minted `pending`. ### 2026-08-24 — Content Guard''s hasPublishedUrl is now recoverability, not the stored URL


    - `GET /content/{id}/monitoring` → `hasPublishedUrl`: was a judgment on the STORED URL (false for a relative path even when the page was live). It now answers "could Guard watch this article right now" — the stored URL when absolute, or an address the server recovers (CMS inventory; FraseCMS re-derivation from current settings when a live published post exists). `true` no longer implies the stored URL itself is absolute, and a FraseCMS article published before domain setup flips to `true` when setup finishes, with no re-publish. It remains exactly the condition enabling enforces. - `publishedUrlIssue` is now the RESIDUAL after recovery failed, not a raw-column classification — "not_absolute" can also mean the FraseCMS post is not live or FraseCMS is disabled for the site. - `POST /content/{id}/publish` → `url` (frase_cms): now derived from the site''s current CMS settings rather than the adapter''s host-of-the-moment value; the same URL is stored on the content row and sent in the content.published webhook.'
  contact:
    name: Frase Support
    url: https://frase.io
    email: support@frase.io
  license:
    name: Proprietary
    url: https://www.frase.io/company/terms
servers:
- url: https://next.frase.io/api/v1
  description: Production
- url: /api/v1
  description: This host
security:
- ApiKeyAuth: []
- BearerAuth: []
tags:
- name: Research
  description: AI-powered research
paths:
  /research:
    get:
      operationId: listResearch
      summary: List research
      description: Retrieve all research sessions in your organization.
      tags:
      - Research
      parameters:
      - name: page
        in: query
        schema:
          type: integer
          default: 1
      - name: page_size
        in: query
        schema:
          type: integer
          default: 20
          maximum: 100
      - name: site_id
        in: query
        schema:
          type: string
          format: uuid
        description: Filter research sessions by site ID
      - name: briefable
        in: query
        schema:
          type: string
          enum:
          - 'true'
          - 'false'
          - '1'
          - '0'
        description: true restricts the list to sessions POST /briefs will accept right now (finished agent sessions; excludes archived sessions and legacy/migrated research whose ids the brief endpoint cannot resolve or whose status is untrustworthy). Build brief-creation pickers from this filter — an id from the unfiltered list can be a guaranteed 404 or 422. Any other value is a 400.
      responses:
        '200':
          description: List of research sessions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedResearch'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      operationId: startResearch
      summary: Start research
      description: Start AI-powered research on a topic. Research runs asynchronously — use the returned session ID with GET /research/{id} to check progress. The research_url field provides a direct link to view the research in the Frase app.
      tags:
      - Research
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - query
              - site_id
              properties:
                query:
                  type: string
                  minLength: 2
                  maxLength: 500
                  description: The topic or search query to research
                site_id:
                  type: string
                  format: uuid
                  description: The site to associate the research with (required). Provides brand context, language, and country defaults.
                type:
                  type: string
                  enum:
                  - general
                  - topic
                  - competitor
                  - gap
                  default: general
                  description: Research type. 'general' runs keywords + SERP + gaps + evidence + AI visibility. 'topic' maps subtopic directions. 'competitor' and 'gap' run the general pipeline with type-specific strategy context.
                language:
                  type: string
                  description: Language code (e.g., 'en'). Falls back to site language if omitted.
                country:
                  type: string
                  description: Country code (e.g., 'US'). Falls back to site country if omitted.
                competitor_domains:
                  type: array
                  items:
                    type: string
                  description: Competitor domains to analyze (used with type=competitor or type=gap)
                audience:
                  type: string
                  description: Target audience context for research
                objective:
                  type: string
                  description: Content objective — what the research should accomplish
                focus:
                  type: string
                  description: Focus area or differentiation lens
      responses:
        '201':
          description: Research started
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      query:
                        type: string
                      status:
                        type: string
                      type:
                        type: string
                        nullable: true
                      language:
                        type: string
                        nullable: true
                      country:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
                      research_url:
                        type: string
                        description: URL to view the research session in the Frase app
                  request_id:
                    type: string
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimited'
  /research/{id}:
    get:
      operationId: getResearch
      summary: Get research
      description: Retrieve a specific research session by ID.
      tags:
      - Research
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Research details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Research'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: deleteResearch
      summary: Delete research
      description: Soft delete a research session (moves to trash).
      tags:
      - Research
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Research deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                  id:
                    type: string
                    format: uuid
        '404':
          $ref: '#/components/responses/NotFound'
  /research/{id}/export:
    get:
      operationId: exportResearch
      summary: Export research
      description: Export research findings in JSON or Markdown format.
      tags:
      - Research
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: format
        in: query
        schema:
          type: string
          enum:
          - json
          - markdown
          default: json
      responses:
        '200':
          description: Exported research
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResearchExport'
            text/markdown:
              schema:
                type: string
        '404':
          $ref: '#/components/responses/NotFound'
components:
  schemas:
    ResearchSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
        query:
          type: string
        status:
          type: string
        type:
          type: string
        language:
          type: string
          nullable: true
        country:
          type: string
          nullable: true
        site_id:
          type: string
          format: uuid
          nullable: true
        created_at:
          type: string
          format: date-time
    PaginatedResearch:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ResearchSummary'
        pagination:
          $ref: '#/components/schemas/Pagination'
        request_id:
          type: string
    Research:
      type: object
      properties:
        id:
          type: string
          format: uuid
        query:
          type: string
        type:
          type: string
          enum:
          - general
          - topic
          - competitor
          - gap
          - serp
          nullable: true
        status:
          type: string
          enum:
          - pending
          - researching
          - analyzing
          - completed
          - failed
          nullable: true
        depth:
          type: string
          nullable: true
        summary:
          type: string
          nullable: true
        findings:
          type: array
          items:
            $ref: '#/components/schemas/ResearchFinding'
        sources:
          type: array
          items:
            $ref: '#/components/schemas/ResearchSource'
        site_id:
          type: string
          format: uuid
          nullable: true
        project_id:
          type: string
          format: uuid
          nullable: true
        language:
          type: string
          nullable: true
        country:
          type: string
          nullable: true
        progress:
          type: integer
          nullable: true
        created_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        data:
          type: object
          nullable: true
          description: Type-specific structured research data. Shape depends on the research type. Null for legacy research sessions.
          properties:
            keywords:
              type: array
              description: Keywords with metrics (type=general)
              items:
                type: object
                properties:
                  keyword:
                    type: string
                  search_volume:
                    type: integer
                  difficulty:
                    type: string
                    enum:
                    - Low
                    - Medium
                    - High
                  cpc:
                    type: number
                  intent:
                    type: string
                    nullable: true
                  category:
                    type: string
            serp_results:
              type: array
              description: SERP competitor analysis (type=general, gap)
              items:
                type: object
                properties:
                  url:
                    type: string
                  title:
                    type: string
                  domain:
                    type: string
                  rank:
                    type: integer
                  word_count:
                    type: integer
                  is_own_site:
                    type: boolean
                  is_tracked_competitor:
                    type: boolean
                  headings:
                    type: array
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                        text:
                          type: string
            evidence:
              type: array
              description: Evidence items (type=general)
              items:
                type: object
                properties:
                  text:
                    type: string
                  type:
                    type: string
                    enum:
                    - stat
                    - fact
                    - quote
                  source:
                    type: string
                    nullable: true
            sources:
              type: array
              description: Sources found (type=general)
              items:
                type: object
                properties:
                  url:
                    type: string
                  title:
                    type: string
                  domain:
                    type: string
            content_gaps:
              type: array
              description: Content gaps (type=gap, general)
              items:
                type: object
                properties:
                  title:
                    type: string
                  description:
                    type: string
                  impact:
                    type: string
                    enum:
                    - high
                    - medium
                    - low
                  competitors_covering:
                    type: integer
            competitors:
              type: array
              description: Competitor analysis (type=competitor)
              items:
                type: object
                properties:
                  domain:
                    type: string
                  url:
                    type: string
                  title:
                    type: string
                  rank:
                    type: integer
                  word_count:
                    type: integer
                  headings:
                    type: array
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                        text:
                          type: string
            topic_directions:
              type: array
              description: Topic exploration directions (type=topic)
              items:
                type: object
                properties:
                  title:
                    type: string
                  description:
                    type: string
                  angle:
                    type: string
                  estimated_difficulty:
                    type: string
                    enum:
                    - Low
                    - Medium
                    - High
            sub_topics:
              type: array
              description: Sub-topic opportunities (type=topic)
              items:
                type: object
                properties:
                  topic:
                    type: string
                  relevance:
                    type: number
                  search_volume:
                    type: integer
            keyword_clusters:
              type: array
              description: Keyword clusters (type=topic)
              items:
                type: object
                properties:
                  cluster_name:
                    type: string
                  keywords:
                    type: array
                    items:
                      type: string
                  total_volume:
                    type: integer
    ResearchSource:
      type: object
      properties:
        url:
          type: string
        title:
          type: string
        snippet:
          type: string
        domain:
          type: string
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
            code:
              type: string
            message:
              type: string
            param:
              type: string
            doc_url:
              type: string
        request_id:
          type: string
    ResearchExport:
      type: object
      properties:
        id:
          type: string
          format: uuid
        query:
          type: string
        type:
          type: string
        depth:
          type: string
        status:
          type: string
        summary:
          type: string
        findings:
          type: array
          items:
            $ref: '#/components/schemas/ResearchFinding'
        topic_clusters:
          type: array
          items:
            type: object
        sources:
          type: array
          items:
            $ref: '#/components/schemas/ResearchSource'
        language:
          type: string
          nullable: true
        country:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        export_format:
          type: string
        exported_at:
          type: string
          format: date-time
    ResearchFinding:
      type: object
      properties:
        title:
          type: string
        content:
          type: string
        relevance:
          type: number
        source_urls:
          type: array
          items:
            type: string
    Pagination:
      type: object
      properties:
        page:
          type: integer
        page_size:
          type: integer
        total:
          type: integer
        has_more:
          type: boolean
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: invalid_request_error
              code: resource_not_found
              message: The requested resource was not found.
              doc_url: https://docs.frase.io/api/errors#resource_not_found
            request_id: req_abc123
    Unauthorized:
      description: Authentication failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: authentication_error
              code: invalid_api_key
              message: Invalid API key. Check that you are using the correct key.
              doc_url: https://docs.frase.io/api/errors#invalid_api_key
            request_id: req_abc123
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until rate limit resets
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: rate_limit_error
              code: rate_limit_exceeded
              message: Rate limit exceeded. Please retry after 60 seconds.
              doc_url: https://docs.frase.io/api/errors#rate_limit_exceeded
            request_id: req_abc123
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              type: invalid_request_error
              code: validation_error
              message: The request body is invalid.
              param: prompt_text
              doc_url: https://docs.frase.io/api/errors#validation_error
            request_id: req_abc123
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY
      description: 'API key for authentication. Get your key from Settings > API Keys. You may also send the same key as `Authorization: Bearer <key>` — see BearerAuth.'
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Frase API key (sk_live_* / sk_test_*) or MCP access token (mcp_at_*)
      description: 'Send either your API key or an MCP access token as `Authorization: Bearer <token>`. An API key sent this way is exactly equivalent to sending it in X-API-KEY — same identity, same scopes, same rate limits — so use whichever your HTTP client makes natural. X-API-KEY takes precedence if you somehow send both. Cookie/session auth is intentionally NOT supported on /api/v1/* endpoints.'