{"id":138,"date":"2026-09-17T15:53:37","date_gmt":"2026-09-17T07:53:37","guid":{"rendered":"https:\/\/wp.qoraapi.com\/vector-database-selection\/"},"modified":"2026-09-20T02:51:19","modified_gmt":"2026-09-19T18:51:19","slug":"vector-database-selection","status":"publish","type":"post","link":"https:\/\/qoraapi.com\/blog\/vector-database-selection\/","title":{"rendered":"How to Choose a Vector Database for RAG"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Choose a vector database by matching four things to your workload: how selective your metadata filters are, whether you need keyword-plus-vector hybrid search, how much recall you will trade for latency, and whether you want to operate the index yourself. Everything else \u2014 brand, benchmark charts, pricing pages \u2014 is downstream of those four.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What a vector database actually does<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A vector database stores a high-dimensional float array per item, a small metadata payload, and an index that answers one query: <em>give me the k items nearest to this query vector<\/em>. If the concepts behind those vectors are new, start with our guide to <a href=\"https:\/\/qoraapi.com\/blog\/ai-embeddings-rag\/\">embeddings and RAG<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Exact nearest-neighbour search is a full scan. At 10M vectors \u00d7 1536 dimensions one query is roughly 15 billion multiply-adds \u2014 hundreds of milliseconds on a CPU, scaling linearly with corpus size. So every production vector database implements <strong>approximate nearest-neighbour (ANN)<\/strong> search: it visits a fraction of the corpus and returns <em>probably<\/em> the true top-k. The discipline is how small that fraction can get before recall collapses.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>HNSW (graph).<\/strong> A multi-layer navigable small-world graph. Queries descend greedily from a sparse top layer to a dense bottom layer while holding a candidate list of size <code>ef_search<\/code>. Query time is roughly logarithmic in corpus size, recall beats every other mainstream index at low latency, and inserts are incremental. Costs: graph and full-precision vectors live in RAM, deletes are tombstoned rather than reclaimed, and every hop is a random access, which makes sharding awkward.<\/li>\n<li><strong>IVF (clustering).<\/strong> k-means over a training sample yields <code>nlist<\/code> centroids; each vector joins its nearest cell, and a query scans only the <code>nprobe<\/code> nearest cells. It composes naturally with product quantization, making it the classic choice for very large, mostly static corpora. Costs: it needs a representative training sample, recall depends heavily on <code>nprobe<\/code>, and inserts drift the centroids, forcing periodic retraining.<\/li>\n<li><strong>Flat (no index).<\/strong> Brute force with SIMD. Exact, zero tuning, fast below a few hundred thousand vectors.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Recall, latency, and memory form a triangle: improve any two at the expense of the third. Raising <code>ef_search<\/code> buys recall with latency. Raising <code>m<\/code> buys recall with memory. Quantizing buys memory with recall. No configuration wins all three, and a vendor claiming otherwise is describing a benchmark dataset, not your data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The selection axes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Seven axes decide almost every real decision. Score your workload on each before you look at a vendor.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"has-fixed-layout\"><thead><tr><th>Axis<\/th><th>What to check<\/th><th>Decision signal<\/th><\/tr><\/thead><tbody><tr><td>Hosting model<\/td><td>Managed, self-hosted, or embedded<\/td><td>Managed wins once ops hours exceed the price delta; self-host when data cannot leave your VPC<\/td><\/tr><tr><td>Hybrid search<\/td><td>Native sparse+dense fusion, or a BM25 sidecar<\/td><td>Required if users search by IDs, error codes, or function names<\/td><\/tr><tr><td>Metadata filtering<\/td><td>Pre-filter vs post-filter, field types, cardinality<\/td><td>Pre-filter is mandatory when a filter keeps under ~10% of the corpus<\/td><\/tr><tr><td>Scale ceiling<\/td><td>Vectors per node, sharding, RAM per vector<\/td><td>Past ~50M vectors at high QPS you need sharded or disk-based indexes<\/td><\/tr><tr><td>Cost model<\/td><td>RAM-hour, per-vector-month, or per-query<\/td><td>Per-query suits spiky traffic; RAM-hour punishes a large idle index<\/td><\/tr><tr><td>Ops burden<\/td><td>Backups, re-index on upgrade, on-call<\/td><td>Engineer-hours per month \u00d7 loaded rate, versus the invoice<\/td><\/tr><tr><td>Multi-tenancy<\/td><td>Namespaces, partitions, isolation<\/td><td>Shared collection plus tenant filter is cheapest until one tenant dominates<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Metadata filtering and hybrid search<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Filtering is where vector search quietly breaks. <strong>Post-filtering<\/strong> retrieves the top-k by distance, then discards rows that fail the filter. That is correct only when the filter keeps most of the corpus: to return k results under a filter with selectivity <em>s<\/em> you must over-fetch roughly k\/s candidates, so at <em>s<\/em> = 0.01 and k = 10 that is 1,000 candidates per query \u2014 and recall is still poor, because the traversal never visited the region where the matching vectors live. You silently get fewer than k results. This is the most common cause of &#8220;our RAG got worse in production but the index metrics look fine&#8221;.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pre-filtering<\/strong> restricts the candidate set first. Naive pre-filtering degenerates into a brute-force scan over the matching subset \u2014 fine when that subset is small, because scanning 1% of 10M vectors exactly beats searching all of them approximately. Production engines combine both, using the filter to seed graph entry points or to select a partition-scoped index. Two rules follow: keep filterable metadata in the vector store&#8217;s payload, not a side table you join afterwards, since a join after the ANN stage means you already paid the recall loss; and partition on the dimension you always filter on.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Hybrid search<\/strong> matters for a different reason: dense embeddings are bad at exact tokens. A part number, a function name, an error code like <code>ERR_4021<\/code>, or a negation lives in the sparse signal, and BM25 or a learned sparse representation catches it. Technical queries are full of literal identifiers that embeddings blur into their neighbours.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fuse the two lists with Reciprocal Rank Fusion, not a weighted score sum. RRF operates on ranks, so BM25&#8217;s unbounded scores and cosine&#8217;s bounded similarities never have to be normalized against each other \u2014 a normalization bug that silently makes hybrid search perform worse than dense-only.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def hybrid_search(query, k=10, alpha=0.7, tenant=None):\n    \"\"\"Fuse dense and sparse retrieval by rank, not by score.\"\"\"\n    qvec = embed(query)\n    dense = vec_index.search(qvec, k=k * 5, filter={\"tenant\": tenant})\n    sparse = bm25.search(query, k=k * 5, filter={\"tenant\": tenant})\n\n    K = 60                                    # RRF constant (Cormack et al.)\n    fused = {}\n    for rank, hit in enumerate(dense):\n        fused[hit.id] = fused.get(hit.id, 0.0) + alpha \/ (K + rank)\n    for rank, hit in enumerate(sparse):\n        fused[hit.id] = fused.get(hit.id, 0.0) + (1 - alpha) \/ (K + rank)\n\n    candidates = sorted(fused.items(), key=lambda kv: -kv[1])[:k * 5]\n    return rerank(query, candidates, top_k=k)  # cross-encoder rescoring\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then rerank the fused candidates with a cross-encoder \u2014 the largest relevance gain in the stack, because it scores each (query, document) pair jointly instead of comparing two independently produced vectors. Embeddings and reranking can come from one OpenAI-compatible endpoint: <a href=\"https:\/\/qoraapi.com\/\" target=\"_blank\" rel=\"noopener\">qoraapi.com<\/a> serves both from a single API key, which also keeps your index and reranker on the same embedding model version. For the pipeline around these pieces, see our guide to <a href=\"https:\/\/qoraapi.com\/blog\/production-rag-architecture\/\">production RAG<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Index types and trade-offs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Sizes below are for 1M vectors at 1536 dimensions in fp32, excluding IDs and payload.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"has-fixed-layout\"><thead><tr><th>Index<\/th><th>RAM \/ 1M vectors<\/th><th>Build<\/th><th>Query knob<\/th><th>Recall@10<\/th><th>Use when<\/th><\/tr><\/thead><tbody><tr><td>Flat (exact)<\/td><td>~6.1 GB<\/td><td>None<\/td><td>None<\/td><td>1.00<\/td><td>Under a few hundred thousand vectors, or as ground truth for recall<\/td><\/tr><tr><td>HNSW, fp32<\/td><td>~6.3 GB<\/td><td>Minutes to hours<\/td><td><code>ef_search<\/code><\/td><td>0.95\u20130.99<\/td><td>Best recall at low latency, incremental writes, up to tens of millions of vectors<\/td><\/tr><tr><td>HNSW + int8<\/td><td>~1.7 GB<\/td><td>Minutes<\/td><td><code>ef_search<\/code><\/td><td>0.93\u20130.98<\/td><td>RAM is the binding constraint and a small recall loss is acceptable<\/td><\/tr><tr><td>IVF-PQ (16 B\/vec)<\/td><td>~0.1 GB<\/td><td>Training + rebuilds<\/td><td><code>nprobe<\/code><\/td><td>0.70\u20130.92<\/td><td>100M+ vectors, mostly static, disk-friendly<\/td><\/tr><tr><td>DiskANN<\/td><td>~0.2 GB + SSD<\/td><td>Hours<\/td><td>Search list size<\/td><td>0.90\u20130.97<\/td><td>Corpus far exceeds RAM, latency budget in tens of ms<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">HNSW&#8217;s three parameters: <code>m<\/code> is graph degree per node \u2014 16 is a sane default, 32\u201364 for high-dimensional data or high recall targets; memory and build time grow linearly with it, but query latency barely moves, because each hop scans only a few more neighbours. Returns diminish above 64. <code>ef_construction<\/code> is the build-time candidate list (100\u2013200 normal, 400+ for hard datasets). <code>ef_search<\/code> is the query-time candidate list \u2014 the knob you tune per query class.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The asymmetry matters: <code>m<\/code> and <code>ef_construction<\/code> are baked into the graph, so changing them means a full rebuild, while <code>ef_search<\/code> is dynamic. The recall curve is concave \u2014 <code>ef_search<\/code> 16 to 64 usually buys most of the available recall, while 256 to 1024 buys a fraction of a point and roughly doubles p99.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- pgvector: build HNSW with explicit params instead of defaults.\n-- m and ef_construction are baked into the graph: changing them = full rebuild.\nCREATE INDEX CONCURRENTLY docs_emb_hnsw\n  ON docs USING hnsw (embedding vector_cosine_ops)\n  WITH (m = 32, ef_construction = 200);\n\nSET hnsw.ef_search = 120;   -- query-time only; raise until recall@10 plateaus\n\n-- Recent pgvector: keep traversing until k rows survive a selective filter,\n-- instead of collecting k candidates and discarding most of them.\nSET hnsw.iterative_scan = strict_order;\n\nSELECT id, 1 - (embedding &lt;=&gt; :query_vec) AS cosine_score\nFROM docs\nWHERE tenant_id = :tenant AND status = 'published'\n  AND embedding &lt;=&gt; :query_vec &lt; 0.35   -- distance ceiling = relevance floor\nORDER BY embedding &lt;=&gt; :query_vec\nLIMIT 10;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When recall drops it is almost always one of five things: <strong>quantization<\/strong>, which discards the low-order components separating near-duplicates (fix: oversample 4\u201310\u00d7 from the compressed index, then rescore against full-precision vectors kept on disk); <strong>stale IVF centroids<\/strong> after bulk inserts; <strong>a selective filter<\/strong> the graph never reaches; <strong>distance-metric mismatch<\/strong>, such as indexing cosine but querying unnormalized vectors; and <strong>model skew<\/strong>, where query vectors come from a different embedding version than the documents.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Measure recall properly: take 1,000\u20135,000 real queries, compute exact top-k with a flat index, then compare. Report recall@k <em>alongside<\/em> p50 and p99 at that recall. A lone &#8220;95% recall&#8221; with no k, no dataset, and no latency is not information.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ops concerns that decide the architecture<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Re-embedding on model change.<\/strong> Swapping the embedding model invalidates every vector \u2014 vectors from two models occupy different spaces, so you cannot mix or reuse them. A model upgrade becomes a full-corpus batch job whose cost scales with corpus tokens, not query volume. Two habits make it survivable: keep the source text of every chunk, and store <code>embedding_model<\/code>, <code>embedding_dim<\/code>, and a <code>chunk_hash<\/code> in the payload so a re-embed skips unchanged chunks. For the cost side, see our guide to <a href=\"https:\/\/qoraapi.com\/blog\/reduce-ai-api-costs\/\">reduce AI API costs<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Versioned collections and alias swaps.<\/strong> Never mutate vectors in place during a migration. Create <code>docs_v2<\/code>, backfill, run your retrieval eval against both, then swap an alias so cutover is atomic and rollback is one line. The same pattern handles index parameter changes, since <code>m<\/code> is fixed at build time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Backups.<\/strong> A vector index is usually rebuildable from source text plus model version \u2014 but only if you kept both. Snapshot the payload as the source of truth; snapshotting the graph is a restore-speed optimization, not a durability strategy. Test a restore, including rebuild time for a large HNSW graph.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Multi-tenancy<\/strong> comes in three patterns, in increasing isolation: a shared collection with a <code>tenant_id<\/code> filter (cheapest, but every query is a filtered ANN query and inherits the selectivity problem above); a namespace per tenant (the index is already scoped, so filtering is free, at the cost of per-tenant overhead and poor cache locality for tiny tenants); and a collection per tenant (strongest isolation, right for a few dozen large tenants, wrong for tens of thousands of small ones). Promote any tenant past ~10% of total vectors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Capacity.<\/strong> RAM is the binding constraint, and a rebuild needs roughly double the steady-state memory. Per-vector-month pricing is comfortable for a demo and punishing for an index that grows while query volume stays flat; RAM-hour billing punishes idle capacity but rewards steady high QPS. Model both at your projected year-two size.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Build vs buy<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A managed vector database pays for itself when<\/strong> the monthly price delta is smaller than your fully loaded engineer-hours for operating a stateful service \u2014 including upgrades that force re-indexing, on-call, failover, and capacity planning. It also wins when traffic is spiky, when you need multi-region replication you have no interest in building, or when nobody wants to own an index.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>pgvector is enough when<\/strong> you already run Postgres and most of these hold: the corpus is under a few million vectors; peak query rate is moderate; you want transactional consistency, so deleting a document deletes its vectors in the same transaction; your filters are naturally SQL; and p99 in the tens of milliseconds is acceptable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Break-even arithmetic, not a price list: 1M vectors at 1536 dimensions in fp32 is about 6 GB before graph overhead. If your database node already has that headroom, self-hosting is probably cheaper. Once you are sharding, running replicas for availability, and rebuilding indexes on every upgrade, the managed price starts to look like a discount. The crossover is not a fixed vector count \u2014 it is the point where your ops hours dominate.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When you do NOT need a dedicated vector database<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most teams asking this question have a corpus small enough that the answer is &#8220;you don&#8217;t&#8221;. Retrieval quality is usually bottlenecked by chunking and embedding choice, not by the index \u2014 moving from flat to HNSW changes latency, not relevance.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Under ~100k chunks.<\/strong> Keep the vectors in memory and brute-force them. 100k \u00d7 1536 fp32 is about 600 MB, and one matrix multiply per query is a few hundred MFLOPs \u2014 tens of milliseconds on one CPU core, with perfect recall and zero tuning. A large share of &#8220;we need a vector database&#8221; projects are 20k chunks.<\/li>\n<li><strong>Per-user corpora.<\/strong> If each user has a few thousand documents, an in-memory index per session or a <code>WHERE user_id = ...<\/code> scan in Postgres is simpler and exact.<\/li>\n<li><strong>Small corpus already in Postgres.<\/strong> pgvector with HNSW, or even a sequential scan, is fine. Do not add a second datastore to search 50,000 rows.<\/li>\n<li><strong>Batch or offline retrieval.<\/strong> If you retrieve once per document inside an offline enrichment job, latency is irrelevant and exact search wins outright.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">You have outgrown the simple options when a few million vectors run under real concurrency and a flat scan blows the latency budget; when hybrid search, filtering, and reranking are first-class requirements rather than features you plan to bolt on; when you need per-tenant isolation and SLOs; or when vector QPS saturates your primary database. Then choose on the axes table above and measure recall on your own queries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Do I need a dedicated vector database for RAG?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For most teams below a few million chunks, no. pgvector or an in-memory flat index gives you exact search, transactional consistency with your app data, and one fewer system to operate. Move on when concurrency, hybrid search, multi-tenancy, or a hard recall\/latency SLO outgrows what your existing database can serve.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">HNSW or IVF?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">HNSW when you need the best recall at low latency and your data changes continuously, because inserts are incremental and no retraining is required. IVF-PQ when memory is the binding constraint or the corpus is mostly static and you can retrain periodically. The two are not exclusive \u2014 several engines run an HNSW graph over quantized vectors.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What recall@k should I target?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Do not target an abstract number; measure against exact kNN on a sample of your own queries. Most RAG pipelines stop seeing end-to-end answer-quality gains somewhere between 0.90 and 0.97 recall@10, because the reranker and the model absorb the remainder. Tune <code>ef_search<\/code> until your own eval plateaus, not until a vendor benchmark says 99%.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I switch embedding models without downtime?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, with versioned collections. Write new chunks to <code>docs_v2<\/code> alongside the live index, backfill the existing corpus while skipping chunks whose hash and model id are unchanged, evaluate retrieval against both, then swap the alias the application reads from. Keep the old collection for one release so rollback is an alias change, not a re-embed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Choosing a vector database is workload matching, not brand selection. Estimate vectors \u00d7 dimensions to size memory, test recall under your most selective filter rather than your average one, decide whether hybrid search is a requirement or a nice-to-have, and compare the managed invoice against your own ops hours. Then measure recall@k and p99 on your own queries \u2014 every other input is someone else&#8217;s benchmark.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Build the retrieval layer on solid concepts first: <a href=\"https:\/\/qoraapi.com\/blog\/ai-embeddings-rag\/\">embeddings and RAG<\/a> for how vectors are produced and compared, then <a href=\"https:\/\/qoraapi.com\/blog\/production-rag-architecture\/\">production RAG<\/a> for the pipeline that wraps the index.<\/p>\n\n\n\n\n<h3 class=\"wp-block-heading\">Related reading<\/h3>\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/qoraapi.com\/blog\/ai-embeddings-rag\/\">AI Embeddings Explained: Vectors, Similarity, and Building Your First RAG<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/production-rag-architecture\/\">Building Production RAG: Chunking, Hybrid Search, and Re-Ranking<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/rag-ingestion-pipeline\/\">Building a RAG Ingestion Pipeline: Crawling, Parsing, and Syncing<\/a><\/li><li><a href=\"https:\/\/qoraapi.com\/blog\/reduce-ai-api-costs\/\">How to Reduce AI API Costs: A Practical Guide for Developers<\/a><\/li><\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Choosing a vector database for RAG comes down to a few axes: managed vs self-hosted, hybrid search, metadata filtering, index type, and cost model.<\/p>\n","protected":false},"author":1,"featured_media":137,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[5,6,9,7],"class_list":["post-138","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-api","tag-ai-api","tag-api-gateway","tag-developer-tools","tag-developers"],"_links":{"self":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts\/138","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/comments?post=138"}],"version-history":[{"count":1,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts\/138\/revisions"}],"predecessor-version":[{"id":192,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/posts\/138\/revisions\/192"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/media\/137"}],"wp:attachment":[{"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/media?parent=138"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/categories?post=138"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qoraapi.com\/blog\/wp-json\/wp\/v2\/tags?post=138"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}