legal.ge Public APIs Matching API

Map a free-text legal question to practice areas, and optionally to ranked verified specialists.

Documentation

Specifications

Other Resources

OpenAPI Specification

legal-ge-public-apis-matching-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: legal.ge Public Matching API
  version: 1.0.0
  summary: Find verified legal specialists in Georgia (the country) from natural-language queries.
  description: Read-only API for finding verified Georgian legal specialists. Designed for AI agents (Perplexity, ChatGPT, Claude, Custom GPTs, MCP servers) and third-party integrations. Supports Georgian, English, and Russian. Sending an inquiry requires sign-in on legal.ge and is not exposed here.
  contact:
    name: legal.ge
    url: https://legal.ge
    email: contact@legal.ge
  license:
    name: API access — no licence required for read-only use
    url: https://legal.ge/terms
  termsOfService: https://legal.ge/terms
  x-logo:
    url: https://legal.ge/apple-touch-icon.png
    altText: legal.ge
    backgroundColor: '#1B2233'
  x-mcp-server:
    npm: '@legalge/mcp'
    repository: https://github.com/infolegalge/legal.ge-mcp
    registry: https://www.npmjs.com/package/@legalge/mcp
  x-llms-txt: https://legal.ge/llms.txt
servers:
- url: https://legal.ge
tags:
- name: Matching
  description: Map a free-text legal question to practice areas, and optionally to ranked verified specialists.
paths:
  /api/ask:
    post:
      operationId: askMatchSpecialists
      tags:
      - Matching
      summary: Classify a free-text legal question and return matching verified specialists.
      description: Pass a natural-language description of the user's legal need (Georgian, English, or Russian). The endpoint maps the text to legal practice areas and returns ranked specialists who work in those areas, including a profile URL for each.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AskRequest'
            examples:
              english:
                value:
                  query: I need LLC registration in Tbilisi
                  locale: en
                  limit: 10
              georgian:
                value:
                  query: მინდა შპს-ის რეგისტრაცია თბილისში
                  locale: ka
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/RateLimited'
    get:
      operationId: askMatchSpecialistsGet
      tags:
      - Matching
      summary: Same as POST /api/ask but with query parameters.
      parameters:
      - name: q
        in: query
        required: true
        schema:
          type: string
          maxLength: 500
      - name: locale
        in: query
        schema:
          type: string
          enum:
          - ka
          - en
          - ru
          default: ka
      - name: limit
        in: query
        schema:
          type: integer
          minimum: 1
          maximum: 50
          default: 20
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AskResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/ask/classify:
    get:
      operationId: classifyLegalIntent
      tags:
      - Matching
      summary: Classify free text into legal practice areas only.
      description: Lighter than /api/ask — returns just the matched categories with weighted scores and the keywords that fired. Useful when an agent wants to pick categories first, then query specialists separately.
      parameters:
      - name: q
        in: query
        required: true
        schema:
          type: string
          maxLength: 500
      - name: locale
        in: query
        schema:
          type: string
          enum:
          - ka
          - en
          - ru
          default: ka
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClassifyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    AskResponse:
      type: object
      properties:
        query:
          type: string
        locale:
          type: string
          enum:
          - ka
          - en
          - ru
        matched_categories:
          type: array
          items:
            $ref: '#/components/schemas/MatchedCategory'
        specialists:
          type: array
          items:
            $ref: '#/components/schemas/AskSpecialist'
          description: Specialists ordered by legal.ge internal relevance ranking. The platform combines category match strength with internal quality and verification signals; exact ranking inputs are intentionally not exposed.
        ranking_note:
          type: string
          description: Human-readable explanation of the ordering convention, suitable for surfacing to end users.
        meta:
          type: object
          properties:
            total:
              type: integer
            returned:
              type: integer
            fallback_used:
              type: boolean
            took_ms:
              type: integer
    ClassifyResponse:
      type: object
      properties:
        query:
          type: string
        locale:
          type: string
          enum:
          - ka
          - en
          - ru
        matched_categories:
          type: array
          items:
            $ref: '#/components/schemas/MatchedCategory'
        meta:
          type: object
          properties:
            fallback_used:
              type: boolean
    AskRequest:
      type: object
      required:
      - query
      properties:
        query:
          type: string
          maxLength: 500
          description: Natural-language legal question or situation.
        locale:
          type: string
          enum:
          - ka
          - en
          - ru
          default: ka
        limit:
          type: integer
          minimum: 1
          maximum: 50
          default: 20
    MatchedCategory:
      type: object
      description: A node matched by the classifier. Can be a category (any depth) or an individual service.
      properties:
        kind:
          type: string
          enum:
          - category
          - service
          description: '`category` = a legal practice area at some depth in the taxonomy. `service` = a specific service under a category. Services are the deepest level the matcher considers.'
        id:
          type: string
          format: uuid
        level:
          type: integer
          description: 'Depth in the taxonomy: 1 = top-level practice area, 2/3 = sub-areas, 4+ = services (one level deeper than their parent category). The matcher narrows ancestors when a descendant matches.'
        slug:
          type: string
        name:
          type: string
        match_confidence:
          type: string
          enum:
          - curated
          - name_fallback
          description: '`curated` = the node was matched via one or more admin-curated keywords (strong signal). `name_fallback` = the matcher only found a token overlap with the node name itself (weaker, used when no curated keyword fired).'
        matched_keywords:
          type: array
          items:
            type: string
          description: Keywords from the user query that triggered this match.
        parent_chain:
          type: array
          description: Ancestors of this node, in child-first order (closest parent first, root last). Lets a caller cite the broader practice area without an extra request.
          items:
            type: object
            properties:
              kind:
                type: string
                enum:
                - category
                - service
              id:
                type: string
                format: uuid
              level:
                type: integer
              slug:
                type: string
              name:
                type: string
    AskSpecialist:
      type: object
      properties:
        id:
          type: string
          format: uuid
        slug:
          type: string
        full_name:
          type: string
        role_title:
          type: string
          nullable: true
        bio_excerpt:
          type: string
          nullable: true
        avatar_url:
          type: string
          nullable: true
        profile_url:
          type: string
          format: uri
        company:
          nullable: true
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              nullable: true
            slug:
              type: string
              nullable: true
        professional_org:
          type: string
          nullable: true
        professional_orgs:
          type: array
          items:
            type: object
            properties:
              org_id:
                type: string
              registration_number:
                type: string
        matched_categories:
          type: array
          items:
            type: string
          description: Slugs of the categories this specialist matched against the query, in arbitrary order.
        contact:
          type: object
          properties:
            email:
              type: string
              nullable: true
            phone:
              type: string
              nullable: true
          description: Populated only when the specialist has opted into public contact display. Otherwise both fields are null and the user must sign in to message via legal.ge.
    ErrorEnvelope:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: 'Machine-readable error code. Stable across versions. Known values: QUERY_REQUIRED, QUERY_TOO_LONG, INVALID_BODY, RATE_LIMITED.'
            message:
              type: string
              description: Human-readable explanation suitable for surfacing to end users.
            field:
              type: string
              description: Name of the offending request field, when the error is field-scoped.
            constraint:
              type: object
              additionalProperties: true
              description: 'Machine-readable constraint that failed. Shape depends on `code`: `{required: true}`, `{max_length: 500}`, `{retry_after_seconds: 42}`, etc.'
            received_length:
              type: integer
              description: 'For QUERY_TOO_LONG: the actual length of the rejected query, so the caller can truncate and retry.'
  responses:
    BadRequest:
      description: 'Invalid request. The `error.code` field is machine-readable; possible values: `QUERY_REQUIRED`, `QUERY_TOO_LONG`, `INVALID_BODY`.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            queryRequired:
              value:
                error:
                  code: QUERY_REQUIRED
                  message: query is required
                  field: query
                  constraint:
                    required: true
            queryTooLong:
              value:
                error:
                  code: QUERY_TOO_LONG
                  message: query exceeds 500 characters
                  field: query
                  constraint:
                    max_length: 500
                  received_length: 712
    RateLimited:
      description: Too many requests. The `Retry-After` header and `error.constraint.retry_after_seconds` both indicate when to retry.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until the next request is allowed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            rateLimited:
              value:
                error:
                  code: RATE_LIMITED
                  message: Too many requests
                  constraint:
                    retry_after_seconds: 42