openapi: 3.0.3
info:
  title: Stackcrawler API
  description: Detect website technologies and retrieve websites by technology.
  version: 1.0.0
  contact:
    url: https://stackcrawler.com/contact
servers:
  - url: https://api.stackcrawler.com
    description: Production
tags:
  - name: Technology lookup
  - name: Website discovery
paths:
  /v1/lookup:
    post:
      tags: [Technology lookup]
      summary: Detect technologies used by a website
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LookupRequest"
            example:
              url: https://shop.benjerry.com
      responses:
        "200":
          description: Website technology results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LookupResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
  /v1/websites-by-technology:
    post:
      tags: [Website discovery]
      summary: Retrieve websites using a technology
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/Limit"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebsitesByTechnologyRequest"
            example:
              technology: nextjs
      responses:
        "200":
          description: Paginated website results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebsitesByTechnologyResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
  /v1/websites-by-theme:
    post:
      tags: [Website discovery]
      summary: Retrieve websites using a theme
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/Limit"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebsitesByThemeRequest"
            example:
              theme: dawn
      responses:
        "200":
          description: Paginated website results for the selected theme
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebsitesByThemeResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
  /v1/technology-migrations:
    get:
      tags: [Website discovery]
      summary: Retrieve newly detected technology migrations
      description: Returns public technologies present in each website's latest snapshot but absent from its previous snapshot.
      security:
        - bearerAuth: []
      parameters:
        - name: technology
          in: query
          description: A public technology slug. Omit to return all technologies.
          schema:
            type: string
          example: shopify
        - name: cursor
          in: query
          description: Opaque cursor returned by a previous filtered response. Used only when technology is supplied.
          schema:
            type: string
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Paginated technology migration results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TechnologyMigrationsResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Your Stackcrawler API key.
  parameters:
    Page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 10
  responses:
    BadRequest:
      description: Invalid request parameters
    Unauthorized:
      description: Missing or invalid API key
    NotFound:
      description: Requested resource was not found
    RateLimited:
      description: Rate or plan request limit reached
    ServerError:
      description: Internal server error
  schemas:
    LookupRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
          format: uri
          description: Website URL to inspect.
        technology:
          type: string
          description: Comma-separated technology slugs to check for.
          example: react,nextjs
        socials:
          type: boolean
          default: false
          description: Include detected social media links.
        contacts:
          type: boolean
          default: false
          description: Include detected contact information.
    Technology:
      type: object
      properties:
        name:
          type: string
        slug:
          type: string
        version:
          type: string
          nullable: true
    LookupResponse:
      type: object
      properties:
        url:
          type: string
          format: uri
        technologies:
          type: array
          items:
            $ref: "#/components/schemas/Technology"
        foundTechnologies:
          type: object
          additionalProperties:
            type: boolean
        socials:
          type: object
          additionalProperties:
            type: string
          nullable: true
        contacts:
          type: object
          additionalProperties: true
          nullable: true
    WebsitesByTechnologyRequest:
      type: object
      required: [technology]
      properties:
        technology:
          type: string
          description: Technology slug.
          example: nextjs
        languages:
          type: string
          example: en
        tlds:
          type: string
          description: Comma-separated top-level domains.
          example: com,org,net
        keywords:
          type: string
          description: Comma-separated keywords matched against URL, title, and description.
          example: blog,shop
        minPerformanceScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxPerformanceScore:
          $ref: "#/components/schemas/LighthouseScore"
        minAccessibilityScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxAccessibilityScore:
          $ref: "#/components/schemas/LighthouseScore"
        minBestPracticesScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxBestPracticesScore:
          $ref: "#/components/schemas/LighthouseScore"
        minSeoScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxSeoScore:
          $ref: "#/components/schemas/LighthouseScore"
    WebsitesByThemeRequest:
      allOf:
        - $ref: "#/components/schemas/WebsiteDiscoveryFilters"
        - type: object
          required: [theme]
          properties:
            theme:
              type: string
              description: Theme slug.
              example: dawn
    WebsiteDiscoveryFilters:
      type: object
      properties:
        languages:
          type: string
          example: en
        tlds:
          type: string
          description: Comma-separated top-level domains.
          example: com,org,net
        keywords:
          type: string
          description: Comma-separated keywords matched against URL, title, and description.
          example: blog,shop
        minPerformanceScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxPerformanceScore:
          $ref: "#/components/schemas/LighthouseScore"
        minAccessibilityScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxAccessibilityScore:
          $ref: "#/components/schemas/LighthouseScore"
        minBestPracticesScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxBestPracticesScore:
          $ref: "#/components/schemas/LighthouseScore"
        minSeoScore:
          $ref: "#/components/schemas/LighthouseScore"
        maxSeoScore:
          $ref: "#/components/schemas/LighthouseScore"
    LighthouseScore:
      type: integer
      minimum: 0
      maximum: 100
    Website:
      type: object
      properties:
        url: { type: string, format: uri }
        language: { type: string, nullable: true }
        title: { type: string, nullable: true }
        metaDescription: { type: string, nullable: true }
        pagespeedPerformanceScore: { type: integer, nullable: true }
        pagespeedAccessibilityScore: { type: integer, nullable: true }
        pagespeedBestPracticesScore: { type: integer, nullable: true }
        pagespeedSEOScore: { type: integer, nullable: true }
        contactEmail: { type: string, nullable: true }
        contactPhone: { type: string, nullable: true }
        websiteCategory: { type: string, nullable: true }
        facebookUrl: { type: string, nullable: true }
        githubUrl: { type: string, nullable: true }
        instagramUrl: { type: string, nullable: true }
        linkedinUrl: { type: string, nullable: true }
        pinterestUrl: { type: string, nullable: true }
        tiktokUrl: { type: string, nullable: true }
        twitterUrl: { type: string, nullable: true }
        youtubeUrl: { type: string, nullable: true }
        pagespeedLastCrawl: { type: string, format: date-time, nullable: true }
        technologies:
          type: array
          items:
            $ref: "#/components/schemas/Technology"
    WebsitesByTechnologyResponse:
      type: object
      properties:
        websites:
          type: array
          items:
            $ref: "#/components/schemas/Website"
        totalWebsites:
          type: integer
        maxPages:
          type: integer
    Theme:
      type: object
      properties:
        name: { type: string }
        slug: { type: string }
        platform: { type: string }
    ThemeWebsite:
      allOf:
        - $ref: "#/components/schemas/Website"
        - type: object
          properties:
            themeVersion: { type: string, nullable: true }
            lastCrawled: { type: string, format: date-time }
    WebsitesByThemeResponse:
      type: object
      properties:
        theme:
          $ref: "#/components/schemas/Theme"
        websites:
          type: array
          items:
            $ref: "#/components/schemas/ThemeWebsite"
        totalWebsites:
          type: integer
        maxPages:
          type: integer
    MigrationWebsite:
      type: object
      properties:
        id: { type: integer }
        url: { type: string, format: uri }
        title: { type: string, nullable: true }
        favicon: { type: string, nullable: true }
        language: { type: string, nullable: true }
        websiteCategory: { type: string, nullable: true }
        version: { type: string, nullable: true }
        addedAt: { type: string, format: date-time }
        tool:
          $ref: "#/components/schemas/Technology"
    Pagination:
      type: object
      properties:
        page: { type: integer }
        limit: { type: integer }
        total: { type: integer }
        totalPages: { type: integer }
    CursorPagination:
      type: object
      properties:
        limit: { type: integer }
        hasPreviousPage: { type: boolean }
        hasNextPage: { type: boolean }
        previousCursor: { type: string, nullable: true }
        nextCursor: { type: string, nullable: true }
    TechnologyMigrationsResponse:
      type: object
      properties:
        tool:
          allOf:
            - $ref: "#/components/schemas/Technology"
          nullable: true
        websites:
          type: array
          items:
            $ref: "#/components/schemas/MigrationWebsite"
        pagination:
          oneOf:
            - $ref: "#/components/schemas/Pagination"
            - $ref: "#/components/schemas/CursorPagination"
