SOSL Limits and Apex Integration
SOSL limits shape the search experience before your Apex code sees a result. This guide separates candidate-result limits from Apex governor limits, then shows how ordering, permissions, and the RETURNING shape affect what users actually receive.
🚦 SOSL Limits on Search Results
Section titled “🚦 SOSL Limits on Search Results”Understanding the limitations of SOSL is crucial for designing efficient and effective search queries. SOSL (Salesforce Object Search Language) limits the number of search results it can return to ensure platform efficiency.
🔢 Maximum Records Returned
Section titled “🔢 Maximum Records Returned”A single SOSL query returns at most 2,000 records in total (API version 28.0 and above). How that cap is split depends on how many objects you search:
- Single object: you get up to 250 records by default. Adding a
WHEREorORDER BYclause raises that ceiling to the full 2,000 records for that object. - Multiple objects: each object returns up to the lesser of 250 records or 2,000 divided by the number of objects. For example, searching 2 objects gives up to 250 each (2,000 ÷ 2 = 1,000, capped at 250), while searching 10 objects gives up to 200 each (2,000 ÷ 10 = 200).
The LIMIT clause can be applied per object in the RETURNING statement or globally for the entire query. However, it cannot increase the overall maximum results beyond 2,000 records.
🔍 Why broad searches can miss records
Section titled “🔍 Why broad searches can miss records”The return caps above are applied after the search engine has already narrowed the field. Salesforce states that the search engine looks for matches to the search term across a maximum of 2,000 records. The per-object caps and user-access filtering are applied to that set, not to every record the term could theoretically match.
The consequence matters more than the mechanism. A very common term can match far more records than the engine considers, so the record you want can be absent before LIMIT or the per-object split is applied at all. This is why a broad term such as Industrial may not surface the exact record you expect, whereas a more selective term such as Industrial Computing San Francisco does. When results look incomplete, narrow the search term rather than raising LIMIT, since LIMIT cannot widen the underlying search scope.
The OFFSET clause skips a specified number of rows so you can paginate through results:
- The maximum offset is 2,000 rows. In Apex, requesting a larger value raises
System.SearchException. - We recommend using
ORDER BYwithOFFSETto ensure consistent ordering, andLIMITwithOFFSETto retrieve subsequent subsets of the same result set. - Re-run the SOSL statement with a larger offset for the next page. Because the search index and matching records can change between calls, later pages can shift; do not use
OFFSETas a durable large-export cursor.
Security note: two separate things decide what a search returns, and it helps to keep them apart.
The first is SOSL’s own filtering. Salesforce states that users with the View All Data permission see the full set of results returned, while for all other users SOSL applies user permission filters. That happens regardless of where the search runs.
The second is the Apex access mode. WITH USER_MODE makes the query enforce the running user’s object, field, and record access explicitly, and an explicitly chosen system mode does not. Declaring the mode keeps that decision visible in the code rather than left to the API version default. Security and SOQL covers the modes in full.
💬 SearchQuery String Limits
Section titled “💬 SearchQuery String Limits”The limits below apply to the text inside FIND {SearchQuery} only, not to the full SOSL statement. Do not confuse them with the overall statement character limit described in the next subsection.
- If the
SearchQuerystring is longer than 10,000 characters, no result rows are returned. - If
SearchQueryis longer than 4,000 characters, any logical operators are removed (for example,ANDdefaults toOR), which can return more results than expected.
Keep search terms concise and test long dynamic queries in a sandbox, especially when building search UIs that concatenate user input.
📏 SOSL Statement Character Limit
Section titled “📏 SOSL Statement Character Limit”The maximum length of a SOSL statement is separate from the SearchQuery limits above. By default, SOSL statements can be up to 100,000 characters, tied to the SOQL statement character limit defined for your org. If a statement exceeds that limit, the API returns a MALFORMED_SEARCH exception and no result rows are returned.
🌐 External Object Search Results
Section titled “🌐 External Object Search Results”External objects come with their own rules, and a search that works on a standard object can behave differently here. The restrictions Salesforce documents fall into three groups: what you set up first, what SOSL can actually search, and which clauses do not work at all.
Set up first
- Search enablement: Search must be enabled on both the external object and its data source.
RETURNINGclause: External objects must be listed explicitly, or they are not returned.
What SOSL can search
- Searchable fields: Only text, text area, and long text area fields.
- Search-term length: Text strings must be 100 or fewer characters.
What does not work
- Unsupported features:
INCLUDES,LIKE,EXCLUDES,toLabel(), and Salesforce Knowledge clauses such asUPDATE TRACKING,UPDATE VIEWSTAT, andWITH DATA CATEGORY. WITHclauses: Not supported at all, so the options covered in the syntax overview do not carry across.- Logical operators on OData: The OData 2.0 and 4.0 adapters for Salesforce Connect do not support them in a
FINDclause, soFIND {Acme AND Cloud}is not evaluated as a boolean expression.
🔧 SOSL in Apex
Section titled “🔧 SOSL in Apex”SOSL queries can be seamlessly integrated into Apex code using the search statement, it allows you to search across multiple objects simultaneously using full-text search capabilities.
SOSL returns List<List<SObject>>, one inner list per object type. Salesforce states that the result lists are always returned in the same order the objects appear in the RETURNING clause, so position, not type, is what tells you which list is which.
📝 Basic Syntax
Section titled “📝 Basic Syntax”// Simple SOSL search across all fieldsList<List<SObject>> searchResults = [ FIND 'Acme*' IN ALL FIELDS RETURNING Account(Id, Name, Industry), Contact(Id, FirstName, LastName, Email) WITH USER_MODE];
// Cast results to specific sObject typesList<Account> accounts = (List<Account>) searchResults[0];List<Contact> contacts = (List<Contact>) searchResults[1];
// No matches means an empty inner list, never null, so no null check is neededif (accounts.isEmpty() && contacts.isEmpty()) { // Nothing the running user can see matched the term}Overall, this code performs a search for records related to “Acme” across all fields in the Account and Contact objects and retrieves specific fields for each object. The second part casts each inner list to its object type. Salesforce states that a search returning no records for an object still includes an empty list for it, so isEmpty() is safe to call straight after the cast without a null guard.
This Apex example demonstrates a typical workflow for using SOSL, highlighting its utility in building dynamic search functionalities within Salesforce applications.
🔗 Bind Variables
Section titled “🔗 Bind Variables”Similarly to SOQL; SOSL supports variable binding using the colon : syntax, allowing dynamic search queries:
// Set bind variablesString term = 'Tech*';Integer minRevenue = 1000000;Integer maxRows = 10;
// SOSL search across name fieldsList<List<SObject>> results = [ FIND :term IN NAME FIELDS RETURNING Account(Id, Name, AnnualRevenue WHERE AnnualRevenue > :minRevenue ORDER BY AnnualRevenue DESC LIMIT :maxRows), Contact(Id, FirstName, LastName, Email) WITH USER_MODE];This code snippet searches for records with names starting with “Tech” in the Account and Contact objects. It uses bind variables for the search term, the revenue filter, and the row limit. For the Account object, it retrieves records with an AnnualRevenue greater than 1,000,000, orders them by revenue in descending order, and limits the results to the top 10. The Contact object results include basic fields without additional filters.
🚩 Key Points
Section titled “🚩 Key Points”- Bracket Notation: Enclose the entire SOSL query in square brackets
- Return Type: The query returns
List<List<SObject>>; each inner list matches one object in theRETURNINGclause. - Variable Binding: Bind
:variableNameinto theFINDsearch term, theWHEREfilter literals andOFFSETinside aRETURNINGblock, and theLIMITat either level. Salesforce’s own example of SOSL binds is labelled “all possible clauses”, so treat that list as the boundary rather than assuming any clause takes a bind. - Casting: Cast each inner list to the specific object type.
- Governor Limits: Up to 20 SOSL queries per Apex transaction; max 2,000 records returned across all objects.
- Error Handling: Treat no matches as empty inner lists, not
null. CatchSearchExceptiononly where a recoverable runtime search error is possible; governor-limit failures are prevented through design rather than caught in Apex. - Performance Considerations: Be mindful of the performance impact of SOSL queries, especially when dealing with large datasets or complex queries. Optimise queries by limiting the number of fields and records returned.
- Security and Permissions: In classes compiled at API 67.0 and later, SOSL defaults to user mode; earlier classes default to system mode. Use
WITH USER_MODEor the dynamicSearchmethod’sAccessLevelexplicitly for user-facing search so the intended sharing, object, and field checks survive API-version changes. - Testing: Thoroughly test SOSL queries in a sandbox environment to ensure they perform as expected and handle edge cases effectively.
In Apex, put the search term in single quotes and require a RETURNING clause. SOAP and REST use curly braces around the search term instead.
🆚 SOSL vs SOQL
Section titled “🆚 SOSL vs SOQL”SOQL follows a clear, structured approach where you specify exactly what object and fields you want to query and also closely aligns with SQL so many developers find it easier to understand. SOSL has a steeper learning curve. As SOSL uses a unique ‘FIND-IN-RETURNING’ structure that differs significantly from traditional query languages, new developers often find this syntax less intuitive than SOQL’s SQL-like approach.
Choosing between SOQL (Salesforce Object Query Language) and SOSL (Salesforce Object Search Language) depends on your specific data retrieval needs, search scope, and performance requirements. Understanding when to use each language is crucial for efficient Salesforce development.
The quickest way to decide is to line the two up against the dimension that matters for your use case:
| Dimension | SOQL | SOSL |
|---|---|---|
| Best for | Structured retrieval when you know the object and fields | Text search across objects when you do not |
| Search scope | One object, or related objects via relationships | Multiple unrelated objects in a single search |
| Matching | Exact predicates and LIKE patterns on named fields | Full-text, tokenised, against the search index |
| Relationships | Parent-to-child and child-to-parent queries | Separate object result lists; supported relationship fields can be returned when relationship queries are enabled |
| Sorting and aggregation | ORDER BY, COUNT(), SUM(), AVG(), GROUP BY | No aggregation; relevance is the default, with per-object ORDER BY available |
| Record cap | 50,000 records per transaction | 2,000 records per SOSL query |
| Queries per transaction | 100 (synchronous) | 20 |
| Big objects | Supported | Not supported |
| International text (Chinese, Japanese, Korean, Thai) | Standard field matching | Morphological tokenisation, a stronger fit |
| Typical use | Reports, list views, record retrieval, data operations | Global search bars, cross-object lookup, duplicate discovery |
The sections below expand on each side of that split.
🧾 What the table does not show
Section titled “🧾 What the table does not show”Two points sit behind those rows and matter more in practice than the numbers themselves.
The record caps are counted differently. SOQL’s 50,000 is a transaction total across every query you run. SOSL’s 2,000 applies to a single search, so each SOSL call in a transaction gets its own ceiling and none of them can be raised. A step that needs more than 2,000 matched records from one search will not get there by adding LIMIT; it needs a narrower term, a smaller RETURNING set, or a different approach entirely.
Speed depends on the search term, not on the language. SOSL is built for tokenised text matching against the search index, and SOQL for selective filters on indexed fields. Neither is reliably faster in the abstract. Benchmark representative terms against production-like data volumes rather than assuming a broad wildcard search will beat a structured query, and see SOQL performance optimisation for how selectivity is judged on the SOQL side.
🏁 Conclusion
Section titled “🏁 Conclusion”Salesforce Object Search Language (SOSL) is an indispensable tool for developers and administrators needing to perform flexible, text-based searches across multiple objects in Salesforce. Its ability to search various field types, utilise wildcards and logical operators, and integrate seamlessly with Apex and other Salesforce APIs makes it highly effective for scenarios where the exact location of data is unknown or when broad search capabilities are required. By understanding SOSL’s syntax, clauses, and limitations, you can leverage its power to build robust and efficient search functionalities within your Salesforce environment, complementing the more structured querying capabilities of SOQL.
This article closes the three-part SOSL series. Revisit the introduction for when to reach for SOSL over SOQL, or the syntax overview for the FIND, IN, RETURNING, and WITH clauses in detail. To apply the same access model to your queries, see Security and SOQL.