6.3 KiB
Extractors API
Base URL
{BASE_URL}
- Local development:
http://localhost:54400 - Docker (internal):
http://extractors:54400 - Production: use your configured hostname (port
14400)
Authentication
None at the service level — access is mediated by the platform API gateway (Bearer token at the perimeter). Do not expose this service directly.
Endpoints
GET /health
Liveness probe.
{ "status": "ok", "version": "0.1.0" }
GET /ready
Readiness + optional tooling availability (informational; the service degrades gracefully if ffprobe/ffmpeg are absent).
{ "status": "ready", "ffprobe": true, "ffmpeg": true }
POST /v1/metadata
Extract deterministic media metadata & integrity features. The applicable extractors are chosen automatically from the detected media type.
Request — multipart/form-data
| Field | Type | Description |
|---|---|---|
file |
file | Image, video or audio file |
curl -X POST http://localhost:54400/v1/metadata \
-F "file=@photo.jpg"
Response 200 OK
{
"filename": "photo.jpg",
"media_type": "image",
"sha256": "9f86d081884c7d65...",
"size_bytes": 84211,
"analyses": {
"integrity": {
"tool_id": "integrity",
"name": "Integrity & signature",
"ok": true,
"results": { "container": "JPEG", "sha256": "9f86...", "size_bytes": 84211 },
"evidence": ["Container: JPEG.", "SHA-256: 9f86d081884c7d65…"],
"anomalies": [],
"confidence": 1.0
},
"exif": {
"tool_id": "exif",
"name": "EXIF metadata",
"ok": true,
"results": {
"format": "JPEG", "dimensions": [4032, 3024],
"make": "Apple", "model": "iPhone 13",
"datetimeoriginal": "2024:06:15 14:30:00",
"gps": { "latitude": 44.426, "longitude": 26.102 },
"software": "Adobe Photoshop 25.0"
},
"evidence": ["Captured with Apple iPhone 13.", "GPS location present: 44.426, 26.102."],
"anomalies": ["Editing/generation software detected in EXIF: Adobe Photoshop 25.0."],
"confidence": 0.95
},
"ela": {
"tool_id": "ela", "name": "Error Level Analysis", "ok": true,
"results": { "quality": 90, "mean_error": 6.1, "max_error": 211.0, "p99_error": 38.0, "hot_pixel_fraction": 0.031 },
"evidence": ["ELA mean error 6.1, p99 38.0 (quality=90)."],
"anomalies": ["Localized high-error region(s) detected — possible splice/edit (3.1% of pixels)."],
"confidence": 0.6
}
},
"evidence": ["..."],
"anomalies": ["..."],
"execution_time_ms": 42.7
}
For video inputs the analyses block contains integrity, video_metadata
(codec/width/height/fps/bitrate/encoder) and spectrogram. For audio:
integrity + spectrogram.
Each extractor returns a uniform FeatureResult:
| Field | Type | Description |
|---|---|---|
tool_id |
string | Stable extractor id |
name |
string | Human-readable name |
ok |
bool | Ran without error |
results |
object | Structured extracted data |
evidence |
string[] | Neutral findings (LLM-readable) |
anomalies |
string[] | Tampering/edit hints |
confidence |
number|null | 0..1 (extraction confidence, not a verdict) |
error |
string|null | Set when ok is false |
POST /v1/sentiment
Classify the sentiment of a text via the LLM gateway (Romanian-aware prompt,
detects irony/sarcasm). Delegated to the LLM gateway — returns 503 if no
gateway is configured.
Request — application/json
| Field | Type | Description |
|---|---|---|
text |
string | Text to analyze (min length 1) |
curl -X POST http://localhost:54400/v1/sentiment \
-H "Content-Type: application/json" \
-d '{"text": "Ce zi frumoasă!"}'
Response 200 OK — a FeatureResult (same schema as above), e.g.:
{
"tool_id": "sentiment",
"name": "Sentiment",
"ok": true,
"results": { "label": "positive", "score": 0.92 },
"evidence": ["Sentiment: positive (0.92)."],
"anomalies": [],
"confidence": 0.92
}
POST /v1/ner
Extract named entities via GLiNER multilingual (urchade/gliner_multi-v2.1).
Requires the optional ml extra — returns 503 if the model is unavailable.
Request — application/json
| Field | Type | Description |
|---|---|---|
text |
string | Text to analyze (min length 1) |
labels |
string[] | null | Entity types to extract (defaults to the RO set) |
curl -X POST http://localhost:54400/v1/ner \
-H "Content-Type: application/json" \
-d '{"text": "Klaus Iohannis și Guvernul României."}'
Response 200 OK — a FeatureResult; results carries the extracted
entities (text, label, span, score).
POST /v1/ocr
Extract visible text verbatim from an image via the LLM vision model.
Delegated to the LLM gateway — returns 503 if no gateway is configured.
Request — multipart/form-data
| Field | Type | Description |
|---|---|---|
file |
file | Image file |
curl -X POST http://localhost:54400/v1/ocr \
-F "file=@scan.png"
Response 200 OK — a FeatureResult; results carries the recognized text.
POST /v1/detect
Detect objects (COCO classes) via YOLO (yolov8n), returning boxes + confidence.
Requires the optional ml extra — returns 503 if the model is unavailable.
Request — multipart/form-data
| Field | Type | Description |
|---|---|---|
file |
file | Image file |
Optional query param: threshold (float, 0..1) — confidence threshold.
curl -X POST "http://localhost:54400/v1/detect?threshold=0.4" \
-F "file=@street.jpg"
Response 200 OK — a FeatureResult; results carries detected objects
(label, box, confidence).
Error Responses
| Status | Meaning |
|---|---|
| 400 | Empty file |
| 413 | File exceeds EXTRACTORS_MAX_UPLOAD_MB |
| 422 | Missing file field |
| 502 | LLMError — the LLM gateway returned an error (/v1/sentiment, /v1/ocr) |
| 503 | LLMNotConfigured (no gateway for /v1/sentiment, /v1/ocr) or the ml extra/model is unavailable (/v1/ner, /v1/detect) |
{ "detail": "empty file" }
Request Headers
| Header | Required | Notes |
|---|---|---|
Content-Type |
yes | multipart/form-data (set by the client) |