---
title: "Classify a free-text legal question and return matching verified specialists."
method: POST
path: "/api/ask"
tags: ["Matching"]
---

# Classify a free-text legal question and return matching verified specialists.

`POST /api/ask`

## Request

- Base URL: `https://legal.ge`
- URL: `https://legal.ge/api/ask`
- Auth: none declared

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.

## Request body

- AskRequest
  - `query` string, required: Natural-language legal question or situation.
  - `locale` 'ka' | 'en' | 'ru'
  - `limit` integer
  - `city` string: Only specialists who work in this city: a slug or a name in any of the three languages. Without it, a city named in the query is used.
  - `language` string: Only specialists who list this working language: a code (`en`, `ru`, `ka`, ...) or its English name. Without it, a language asked for in the query is used.

## Response `200`

OK

- AskResponse
  - `query` string
  - `locale` 'ka' | 'en' | 'ru'
  - `matched_categories` MatchedCategory[]
    - `kind` 'category' | 'service': `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` string, uuid
    - `level` integer: 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` string
    - `name` string
    - `match_confidence` 'curated' | 'name_fallback': `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` string[]: Keywords from the user query that triggered this match.
    - `parent_chain` object[]: 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.
      - `kind` 'category' | 'service'
      - `id` string, uuid
      - `level` integer
      - `slug` string
      - `name` string
  - `specialists` AskSpecialist[]: 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.
    - `id` string, uuid
    - `slug` string
    - `full_name` string
    - `role_title` string, nullable
    - `bio_excerpt` string, nullable
    - `avatar_url` string, nullable
    - `profile_url` string, uri
    - `company` object, nullable
      - `id` string, uuid
      - `name` string, nullable
      - `slug` string, nullable
    - `professional_org` string, nullable
    - `professional_orgs` object[]
      - `org_id` string
      - `registration_number` string
    - `matched_categories` string[]: Slugs of the categories this specialist matched against the query, in arbitrary order.
    - `contact` object: Masked (for example `n***@gmail.com`, `+995 5XX XX XX XX`) for each channel the specialist publishes; null for a channel they do not. The full contact is revealed on the `profile_url`.
      - `email` string, nullable
      - `phone` string, nullable
    - `languages` string[], nullable: Working languages the listing card shows, as BCP 47 codes. Null when the listing could not be read.
    - `cities` object[], nullable: Cities the specialist works in. Null when the listing could not be read.
      - `slug` string
      - `name` string
    - `consultation_price` object, nullable: The consultation price the listing card shows: `free`, or a `fee` in GEL (`from` = the lowest of several options), or neither = by agreement. Null when the listing could not be read.
      - `fee` number, nullable
      - `free` boolean
      - `from` boolean
      - `currency` 'GEL'
    - `bar_register_matched` boolean, nullable: legal.ge matched this person's name to an entry in the Georgian Bar Association's public register and a person on its team confirmed it. legal.ge's own check, not a statement by the Bar Association. Null when the listing could not be read.
  - `filters` object: The filters applied: from the parameters (`source: param`) or read from the query (`source: text`). Words that only name a city, a language or a price are not matched as legal matters.
    - `city` object, nullable
      - `slug` string
      - `name` string
      - `source` 'param' | 'text'
    - `language` object, nullable
      - `code` string
      - `source` 'param' | 'text'
    - `fee_question` boolean: The query asks what a lawyer costs. With no legal matter in it, only specialists who publish a price are returned.
  - `fee_facts` object, nullable: For a price question: the consultation fees the listing shows for the specialists in scope (after the city and language filters). Null otherwise.
    - `publishing` integer: Specialists who publish a paid fee
    - `min` number, nullable
    - `max` number, nullable
    - `median` number, nullable
    - `free` integer: Specialists whose first consultation is free
    - `by_agreement` integer: Specialists who set the price by agreement
    - `currency` 'GEL'
  - `ranking_note` string: Human-readable explanation of the ordering convention, suitable for surfacing to end users.
  - `meta` object
    - `total` integer
    - `returned` integer
    - `fallback_used` boolean
    - `took_ms` integer

## Other responses

- `400`: Invalid request. The `error.code` field is machine-readable; possible values: `QUERY_REQUIRED`, `QUERY_TOO_LONG`, `INVALID_BODY`.
- `429`: Too many requests. The `Retry-After` header and `error.constraint.retry_after_seconds` both indicate when to retry.
- `503`: A `city` or `language` filter (from a parameter or the question) could not be applied because the specialist listing could not be read. Never answered with an unfiltered list instead. Retry after `Retry-After` seconds.

---

[API](https://skmtc.dev/legal-ge/apis/legal-ge-public-api.md) · [All operations](https://skmtc.dev/legal-ge/apis/legal-ge-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/legal-ge/apis/legal-ge-public-api/revisions/060000c9c51b?raw)
