Skip to main content
Features answer the third of the three pricing questions — after what kind of files and how much content comes what you want to search by. Instead of choosing models or pipelines, you declare the capabilities you want — visual similarity (image_search), faces (faces), on-screen text (onscreen_text), document layout (document_layout) — and the platform resolves the implementation internally. Swapping or upgrading the models behind a feature never changes your config, your API calls, or your pricing. Every feature applies to a modality (the what kind of files question) and is billed in that modality’s natural unit (the how much question):
ModalityUnitBase feature (included in base rate)Add-on features
imageper imageimage_searchfaces, multimodal_understanding
videoper minutevideo_searchfaces, onscreen_text, audio_fingerprint, multimodal_understanding
audioper minuteaudio_searchmultimodal_understanding
documentper pagedocument_searchfaces, document_layout, multimodal_understanding
textper tokentext_searchmultimodal_understanding
webper crawled pageweb_crawl
Clustering and taxonomy enrichment are included on every modality at no additional charge. For rates, tiers, and how usage is billed, see Billing & Pricing.

Discover features

GET /v1/collections/features returns the live feature menu — modalities, units, feature keys, display names, and current rates. No authentication required. This endpoint is the source of truth: studio, the homepage pricing page, and the billing engine all read the same catalog.
curl "https://api.mixpeek.com/v1/collections/features"
{
  "modalities": [
    {
      "modality": "video",
      "unit": "minute",
      "unit_display": "per minute",
      "base": {
        "key": "video_search",
        "name": "Video search (scene embeddings)",
        "kind": "base",
        "included_in_base_rate": true,
        "rate_usd": 0.05,
        "per": 1
      },
      "addons": [
        { "key": "faces", "name": "Face detection + identity", "kind": "addon", "rate_usd": 0.10, "per": 1, "companion": true },
        { "key": "onscreen_text", "name": "On-screen text (video OCR)", "kind": "addon", "rate_usd": 0.10, "per": 1, "companion": true },
        { "key": "clustering", "name": "Clustering (included)", "kind": "included", "included": true, "pricing_note": "included — no additional charge" }
      ]
    }
  ],
  "custom_features": [],
  "full_res_multiplier": 2.0
}
Response abbreviated — the live payload covers all six modalities. Feature kind tells you how it’s priced:
KindMeaning
baseCovered by the modality’s per-unit base rate — you get it by creating any collection for that modality
addonPriced per the same unit, added on top of the base rate
externalBacked by external LLM inference — usage-based passthrough pricing, never flat-rated
includedBundled enrichment (clustering, taxonomy) — no additional charge

Create a collection with features

Pass features: [...] to Create Collection. The platform resolves the pipeline, pins its version, and defaults the input wiring for you:
curl -X POST "https://api.mixpeek.com/v1/collections" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "collection_name": "press-photos",
    "source": { "type": "bucket", "bucket_ids": ["'$BUCKET_ID'"] },
    "features": ["image_search"]
  }'
Both SDKs accept features on collection create — Python mixpeek>=1.3.29 (client.collections.create(...)) and JavaScript mixpeek>=0.81.28 (client.collections.createCollection({ createCollectionRequest: { ... } })). Set the namespace when you construct the client (Python namespace=, JS namespace:, or the MIXPEEK_NAMESPACE env var) — collection create is namespace-scoped and returns 403 without it. The feature_extractor field is a deprecated alias.

Default input wiring

Every organization’s scaffolded uploads bucket exposes one standard source property per modality — image, video, audio, pdf, content (text), and url (web). A features-based create maps the pipeline’s inputs to those properties automatically, so the collection is runnable with zero wiring. If your bucket uses different property names, the create returns a teaching 422 naming the fields that exist so you can rename the property — or fall back to explicit input_mappings via the advanced pipeline config.

Errors teach

Validation on the features path is designed for humans and agents guessing their way in — every rejection tells you what to do instead:
  • Unknown keyfeatures: ["face"] responds with the full list of valid feature keys (and reminds you about custom:<plugin>).
  • One pipeline per collection — some feature combinations need separate collections (e.g. a base feature plus a companion: true add-on). The error lists exactly which features group into which collection: create one collection per group over the same source bucket. Add-ons flagged companion: true in discovery always create a companion collection alongside the base.
  • features XOR feature_extractor — provide one or the other, never both.
  • Not yet available — feature keys that appear on the roadmap but haven’t shipped are rejected explicitly rather than silently ignored.

Custom features (bring your own)

Custom extractors you publish are your vocabulary — they stay explicitly named and are selected as custom:<plugin_name>:
{
  "collection_name": "product-defects",
  "source": { "type": "bucket", "bucket_ids": ["bkt_123"] },
  "features": ["custom:defect_detector"]
}
Custom features are priced per unit from the compute profile the plugin declares — the same machinery that prices native features. See Custom Extractors.

Full resolution opt-out

By default, Mixpeek normalizes content once at ingest (video to a 720p mezzanine, images capped at ~1568px max edge, audio to 16kHz mono) — originals are always kept untouched in your bucket. If a workload needs extraction on the original resolution (fine print OCR, tiny logos), opt out per collection:
{
  "collection_name": "fine-print",
  "source": { "type": "bucket", "bucket_ids": ["bkt_123"] },
  "features": ["document_layout"],
  "full_res": true
}
Full-res processing bills at a 2x multiplier on the modality’s base and add-on rates (full_res_multiplier in the pricing payload).

Chain collections (advanced)

Searching by one thing often produces content worth searching by another — e.g. search video by scenes, then feed those documents into a second collection to search the results by something else. Collection-to-collection pipelines do exactly this: a collection can read another collection’s documents as its source, forming a processing DAG. See Multi-Tier Feature Extraction. A feature_search stage needs a feature_uri — the exact vector index to search. You picked features by name; each one produces a vector index whose feature_uri looks like mixpeek://<extractor>@<version>/<model>.
The last segment is the embedding model name, not the literal word embedding. A wrong feature_uri (e.g. .../embedding) does not error — the stage matches nothing and silently returns 0 results (with a self-correcting warning in the response). Always use the exact string.
The reliable, version-proof way — read it from the collection. After processing, GET the collection and copy the URI; this always matches what you actually built, even if model versions change:
curl "https://api.mixpeek.com/v1/collections/$COLLECTION_ID" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID"
# → vector_indexes[].feature_uri  e.g. "mixpeek://universal_extractor@v1/gemini-embedding-2"
Common features → feature_uri (the exact string a fresh @v1 collection produces):
Feature (features: [...])Extractorfeature_uri
video_searchuniversal_extractormixpeek://universal_extractor@v1/gemini-embedding-2
text_searchtext_extractormixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1
multimodal_understandingmultimodal_extractormixpeek://multimodal_extractor@v1/vertex_multimodal_embedding
Video transcript search. video_search produces one blended scene embedding (gemini-embedding-2); the transcript rides along as a text payload field, not a separate searchable vector. To search a transcript as its own vector, add the multimodal_understanding feature with transcription enabled — it creates a second index mixpeek://multimodal_extractor@v1/multilingual_e5_large_instruct_v1. When in doubt, GET the collection and read the feature_uris it actually has.

Estimate before you run

POST /v1/organizations/billing/estimate quotes planned ingestion using the same rating engine that bills you — the quote and the charge can’t disagree:
curl -X POST "https://api.mixpeek.com/v1/organizations/billing/estimate" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "mime_type": "video/mp4", "minutes": 42 },
      { "mime_type": "application/pdf", "pages": 300 }
    ],
    "features": ["faces", "onscreen_text"]
  }'
{
  "pricing_model": "v2",
  "placeholder_rates": false,
  "line_items": [
    { "modality": "video", "key": "video_search", "units": 42.0, "rate_usd": 0.05, "per": 1, "amount_usd": 2.10 },
    { "modality": "video", "key": "faces", "units": 42.0, "rate_usd": 0.10, "per": 1, "amount_usd": 4.20 },
    { "modality": "video", "key": "onscreen_text", "units": 42.0, "rate_usd": 0.10, "per": 1, "amount_usd": 4.20 },
    { "modality": "document", "key": "document_search", "units": 300.0, "rate_usd": 1.50, "per": 1000, "amount_usd": 0.45 },
    { "modality": "document", "key": "faces", "units": 300.0, "rate_usd": 1.00, "per": 1000, "amount_usd": 0.30 }
  ],
  "total_usd": 11.25,
  "batch_minimum_applied": false,
  "allowance_pool_usd": 10.0,
  "allowance_covered_usd": 10.0,
  "estimated_overage_usd": 1.25
}
Base features are quoted automatically per the modality of each item — you only list add-ons. Unknown MIME types quote at the cheapest band rather than failing (be forgiving is a design rule here).

How this relates to extractors

Under the hood every feature resolves to an extraction pipeline, and custom extractors let you register your own. But built-in extractor names are no longer part of the public API surface — they remain accepted in configs only as deprecated aliases. If you have existing configs using feature_extractor, see the migration guide; for advanced pipeline knobs (input mappings, field passthrough, parameters), see Pipeline Configuration.