{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/desearch-ai/json-schema/desearch-ai-people-task-schema.json",
  "title": "PeopleTask",
  "description": "A People Search task. Poll it until `status` is completed, partial, failed or cancelled.",
  "x-generated": "2026-10-30",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/desearch-ai-openapi.json#/components/schemas/PeopleTask",
  "properties": {
    "task_id": {
      "type": "string",
      "format": "uuid",
      "title": "Task Id",
      "description": "Id of this search, returned as soon as it is created. Pass it to `GET /desearch/people/tasks/{task_id}`, the results and export routes, and pause, resume, or cancel."
    },
    "search_type": {
      "type": "string",
      "enum": [
        "x_profile_search",
        "youtube_creator_search",
        "linkedin_profile_search",
        "tiktok_creator_search"
      ],
      "title": "Search Type"
    },
    "platform": {
      "type": "string",
      "enum": [
        "x",
        "youtube",
        "linkedin",
        "tiktok"
      ],
      "title": "Platform"
    },
    "status": {
      "type": "string",
      "enum": [
        "queued",
        "running",
        "pausing",
        "paused",
        "cancelling",
        "completed",
        "partial",
        "failed",
        "cancelled",
        "expired"
      ],
      "title": "Status"
    },
    "stage": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "planning",
            "searching",
            "processing",
            "finalizing"
          ]
        },
        {
          "type": "null"
        }
      ],
      "title": "Stage"
    },
    "query": {
      "type": "string",
      "title": "Query"
    },
    "count": {
      "type": "integer",
      "title": "Count"
    },
    "result_count": {
      "type": "integer",
      "title": "Result Count"
    },
    "metadata": {
      "additionalProperties": {
        "type": "string"
      },
      "type": "object",
      "title": "Metadata",
      "description": "The `metadata` sent when the search was created (`{}` when none)."
    },
    "interpreted": {
      "anyOf": [
        {
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "title": "Interpreted",
      "description": "How the search was understood: criteria and the queries it ran."
    },
    "progress": {
      "type": "object",
      "title": "Progress",
      "description": "Counters while the search runs (candidates found and processed, filters, enrichment per platform) and `stop_reason`, the worker's internal reason for stopping. Prefer `completion_reason` and `coverage_limited`."
    },
    "errors": {
      "items": {},
      "type": "array",
      "title": "Errors"
    },
    "completion_reason": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "count_reached",
            "rounds_exhausted",
            "no_candidates",
            "zero_qualified",
            "candidate_budget",
            "cost_limit",
            "time_limit",
            "cancelled",
            "failed"
          ]
        },
        {
          "type": "null"
        }
      ],
      "title": "Completion Reason",
      "description": "Why the search ended; `null` while it is queued, running or paused. `count_reached`: it found `count` profiles. `rounds_exhausted`: it ran every search it planned and found fewer than `count`. `no_candidates`: its searches found nobody to check. `zero_qualified`: it checked the people its searches found and none fit. `candidate_budget`: it checked as many candidates as a search of this `count` may (min(1000, max(150, count x 8))). `cost_limit`: what searching cost reached what its results would bill, so it stopped. `time_limit`: it ran for its maximum time. `cancelled`: it was cancelled. `failed`: an error ended it (every search failed, or an internal error). Added alongside `status`, which keeps its values: a search that stopped on a budget with fewer than `count` profiles, or with none, is still `completed` — see `coverage_limited`."
    },
    "coverage_limited": {
      "type": "boolean",
      "title": "Coverage Limited",
      "description": "True when the search stopped on a budget (`completion_reason` `cost_limit`, `candidate_budget` or `time_limit`) before reaching `count`: there may be more matching people than it returned.",
      "default": false
    },
    "warnings": {
      "items": {
        "$ref": "#/$defs/PeopleTaskWarning"
      },
      "type": "array",
      "title": "Warnings",
      "description": "What made part of the search less reliable without failing it, e.g. LinkedIn profiles that could not be read (`partial_enrichment`). `[]` when nothing did."
    },
    "reserved_cost_usd": {
      "type": "number",
      "title": "Reserved Cost Usd",
      "description": "Amount reserved when the task was created."
    },
    "final_cost_usd": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Final Cost Usd",
      "description": "Final cost once the task has ended. The rest of the reservation is returned."
    },
    "refunded_usd": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Refunded Usd"
    },
    "created_by": {
      "anyOf": [
        {
          "$ref": "#/$defs/PeopleTaskCreatedBy"
        },
        {
          "type": "null"
        }
      ]
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "title": "Created At"
    },
    "started_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ],
      "title": "Started At"
    },
    "paused_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ],
      "title": "Paused At"
    },
    "finished_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ],
      "title": "Finished At"
    },
    "results_expire_at": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ],
      "title": "Results Expire At"
    },
    "links": {
      "anyOf": [
        {
          "$ref": "#/$defs/PeopleTaskLinks"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "additionalProperties": true,
  "type": "object",
  "required": [
    "task_id",
    "search_type",
    "platform",
    "status",
    "query",
    "count",
    "result_count",
    "reserved_cost_usd",
    "created_at"
  ],
  "$defs": {
    "PeopleTaskCreatedBy": {
      "properties": {
        "user_id": {
          "anyOf": [
            {
              "type": "string",
              "format": "uuid"
            },
            {
              "type": "null"
            }
          ],
          "title": "User Id"
        },
        "api_key_id": {
          "anyOf": [
            {
              "type": "string",
              "format": "uuid"
            },
            {
              "type": "null"
            }
          ],
          "title": "Api Key Id"
        },
        "via": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Via"
        }
      },
      "type": "object",
      "title": "PeopleTaskCreatedBy"
    },
    "PeopleTaskLinks": {
      "properties": {
        "self": {
          "type": "string",
          "title": "Self"
        },
        "results": {
          "type": "string",
          "title": "Results"
        },
        "export": {
          "type": "string",
          "title": "Export"
        }
      },
      "type": "object",
      "required": [
        "self",
        "results",
        "export"
      ],
      "title": "PeopleTaskLinks"
    },
    "PeopleTaskWarning": {
      "properties": {
        "code": {
          "type": "string",
          "title": "Code",
          "description": "`partial_enrichment`: some candidates' profiles could not be read after search, so they were scored on what the search returned (results among them have `attributes.enrichment` = `failed`)."
        },
        "platform": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "x",
                "youtube",
                "linkedin",
                "tiktok"
              ]
            },
            {
              "type": "null"
            }
          ],
          "title": "Platform"
        },
        "attempted": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Attempted",
          "description": "Profile lookups tried (YouTube: channels)."
        },
        "enriched": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Enriched",
          "description": "Lookups that read the profile."
        },
        "failed": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Failed",
          "description": "Lookups that could not read the profile."
        },
        "by_reason": {
          "additionalProperties": {
            "type": "integer"
          },
          "type": "object",
          "title": "By Reason",
          "description": "LinkedIn and TikTok: failed lookups by reason — `not_found` (private, removed, or not shown to logged-out visitors), `rate_limited`, `timeout`, `upstream_error`, `request_error`. YouTube: failed lookups by source (`about_page_failed`, `innertube_failed`); a channel still counts as enriched when another source answered, but may miss its About description, links or emails."
        },
        "message": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Message"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "code"
      ],
      "title": "PeopleTaskWarning",
      "description": "Something that made part of a search less reliable without failing it."
    }
  }
}