Solr API

Opensolr Solr API — find answers to your questions

Querying the Solr API: Search Parameters Explained

Every Opensolr Index answers searches at its own /select URL. The hosted search page, the chat bot and the Drupal and WordPress plugins use it, and your own code can use it too. This page covers the request and its search parameters; the pages linked at the end cover each feature: hybrid search, highlighting, faceting, pagination, sorting and spellcheck.

01 · The request

Send a GET or a POST to the /select URL of your index. YOUR_INDEX_HOST is the host shown in the Control Panel of the index. Every request carries the username and password of the index (HTTP Basic Authentication).

curl -u "opensolr:YOUR_API_KEY" "https://YOUR_INDEX_HOST/solr/YOUR_INDEX/select" \
  --data-urlencode "q=solar panels" \
  --data-urlencode "fl=id,uri,title,description,score" \
  --data-urlencode "rows=10" \
  --data-urlencode "wt=json"
Keep the API key and the password of the index on your server. A web page shows its JavaScript to every visitor, and the password of a new index is your API key: the username and password of your index.

02 · Copy the request of your search page

The hosted search page of your index, https://search.opensolr.com/YOUR_INDEX, shows the exact request it sent for every search:

01

Run a search on the page.

02

Click { } Debug in the bottom left corner. The Query Inspector opens with every parameter and its value.

03

Click Copy Params to copy them all.

Start from these: they already carry the Search Tuning of your index (field weights, minimum match, search mode).

03 · The parameters that matter

ParameterWhat it doesExample
qWhat to search for. With the hybrid parser it holds the hybrid query.q=solar panels
fqA filter: keeps only the documents that match, does not change the ranking, and is cached by Solr. Several fq must all match.fq=content_type:text*
flThe fields to send back. Ask only for what you show.fl=id,uri,title,score
rows, startHow many results, and from which position.rows=10&start=20
sortAn order other than relevance.sort=creation_date desc
wtThe format of the answer.wt=json

04 · Filters you will use

fq=content_type:text*                    web pages only (the Web tab of the hosted page)
fq=-content_type:text*                   documents only: PDF, Word, Excel... (the Documents tab)
fq={!term f=meta_detected_language}en    one language
fq={!term f=meta_domain}example.com      one site, when the index holds several
fq=creation_date:[NOW-7DAYS TO *]        published in the last 7 days
fq=-uri_s:*/tag/*                        leave out every address containing /tag/

The {!term} form takes the value exactly as written, so a value your page received from a facet can go straight into the filter.

05 · Filters on every search

To leave pages out of every search without changing your code, add the filter to search.xml in the Configuration tab of your index, in the appends list of the /select handler. Saving the file reloads the index, and the filter applies to every search from then on, the hosted search page included.

<lst name="appends">
    <str name="fq">-uri_s:*/tag/*</str>
    <str name="fq">-uri_s:*/cart*</str>
</lst>

To keep a page out of the index itself, use the rules of the crawl instead: rules per site.

06 · Next

Hybrid search parameters

The {!hybrid} parser: words and meaning in one request.

Highlighting

Snippets with the searched words marked.

The JSON response

What each part of the answer holds.

Facets

Counts per value and filters.

Pagination

rows, start and cursorMark.

Sorting

By date, price or any field.

Fresh results

Lift newer documents.

Autocomplete

Suggestions while typing.

Did you mean

Spelling corrections.

Code examples

PHP, Node.js, Python and curl.

No Solr request to build at all: search from your own code with one API call that embeds and searches.

Read Full Answer

Understanding the Solr JSON Response

Ask with wt=json and every answer of your index is built from the same parts. This is what each one holds.

01 · The parts

PartWhenWhat it holds
responseHeaderAlwaysstatus 0 means success. QTime is the time Solr spent, in milliseconds, network not included.
responseAlways, unless there is an errornumFound (all the matches), start (the offset you asked for), maxScore (when fl asks for the score) and docs, the results of this page.
highlightinghl=trueSnippets with the searched words marked, keyed by document id: Highlighting.
facet_countsfacet=trueCounts per value: Facets.
spellcheckspellcheck=trueCorrections: Did you mean.
errorOn failureThe reason and the HTTP code.

02 · A result

{
  "id": "1f9e6cba8357fe106a950a1f80b301cc",
  "uri": "https://www.example.com/blog/solar-panels",
  "title": "Solar Panels for Small Roofs",
  "description": "How many panels fit on a small roof, and what they produce.",
  "og_image": "https://www.example.com/img/solar.jpg",
  "meta_domain": "example.com",
  "meta_detected_language": "en",
  "content_type": "text/html",
  "creation_date": "2026-09-14T08:30:00Z",
  "score": 0.853
}

A result carries the fields named in fl; score is there only when fl asks for it. What each field holds: the core fields and the fields Opensolr adds to every document.

03 · Facets

Each facet is a flat list of value, count, value, count:

"facet_counts": {
  "facet_fields": {
    "meta_detected_language": ["en", 834, "de", 245, "fr", 123]
  }
}
var pairs = data.facet_counts.facet_fields.meta_detected_language;
var facets = [];
for (var i = 0; i < pairs.length; i += 2) {
  facets.push({ value: pairs[i], count: pairs[i + 1] });
}

With json.nl=map in the request, the same facet comes back as an object instead: {"en": 834, "de": 245}.

04 · Did you mean

"spellcheck": {
  "suggestions": ["bakup", {"numFound": 3, "startOffset": 5, "endOffset": 10, "suggestion": ["backup", "backups", "baked"]}],
  "collations": ["collation", "solr backup", "collation", "solr backups"]
}

collations alternates the word collation with a whole corrected search; the first one is collations[1].

05 · Errors

{
  "responseHeader": { "status": 400, "QTime": 36 },
  "error": {
    "metadata": ["error-class", "org.apache.solr.common.SolrException", "root-error-class", "org.apache.solr.common.SolrException"],
    "msg": "undefined field nonexistent_field",
    "code": 400
  }
}

The HTTP status of the answer is the same code. 400 means the request is wrong (syntax, a field that does not exist, a bad value); 401 means the username or password is wrong; 500 is an error inside Solr, and your Error Log shows it.

Read Full Answer

Pagination: rows, start and cursorMark

Solr sends results one page at a time: rows says how many, start says from which position, counting from 0.

01 · rows and start

PageParametersResults
1start=0&rows=101 to 10
2start=10&rows=1011 to 20
Nstart=(N−1)×rows
var rows = 10;
var pages = Math.ceil(data.response.numFound / rows);
var current = Math.floor(data.response.start / rows) + 1;

02 · Deep pages cost more

To answer start=100000&rows=20, Solr ranks the first 100,020 results again, on every request. Show a few hundred pages at most and refuse deeper requests in your own code. Opensolr checks the traffic of every index daily and emails you when it sees requests deeper than start=100000, which is how a scraper walking your index usually looks.

03 · Every document: cursorMark

To read a whole index (an export, a migration), use a cursor. Its cost stays the same on every page, however deep:

q=*:*&sort=id asc&rows=500&cursorMark=*
01

Each answer carries nextCursorMark. Send it as cursorMark in the next request, with the same query and sort.

02

When nextCursorMark comes back equal to the one you sent, you have every document.

03

The sort must end with id, and the request must not send start.

04 · Next

Sorting

Order results by date, price or any field.

Querying the Solr API

The request and the parameters that matter.

Results per page

How many results the hosted search page shows.

Read Full Answer

Loading more articles...