The short version: SOSL and global search read a search index that is built asynchronously after a record is saved, so a record can exist, be queryable by SOQL, and still not appear in search for a short period. Nothing is broken. If your process needs a record found immediately after creation, use SOQL on an indexed field, not search.
Two different systems, one mental model
SOQL runs against the database. SOSL and the global search box run against a search index that is populated from the database after the fact. That asynchronous step is where every surprising behaviour in this article comes from.
| SOQL | SOSL / global search | |
|---|---|---|
| Reads from | The database | The search index |
| Freshness | Immediate, transactional | Eventually consistent |
| Matching | Exact, with LIKE wildcards |
Tokenised, with stemming and synonyms |
| Scope | One object per query | Many objects at once |
| Best at | “Give me the records meeting these criteria” | “Find the thing containing roughly this text” |
The lag is real and not documented as a number
Salesforce does not publish a guaranteed indexing latency, and you should not design against one you measured. It is usually fast enough that nobody notices, and it degrades under load — which means the failure arrives during a bulk load or at month end, not during testing.
The honest engineering position: treat search as eventually consistent with an unbounded delay, and never put it on a path where a missing result causes a wrong outcome.
Where this bites
A test that creates a record then searches for it
Account a = new Account(Name = 'Test Account');
insert a;
List<List<SObject>> found = [FIND 'Test Account' IN ALL FIELDS RETURNING Account(Id)];
// Empty. The index does not exist inside the test transaction.
Apex tests do not index records at all. The fix is Test.setFixedSearchResults, which tells the runtime what SOSL should return:
Account a = new Account(Name = 'Test Account');
insert a;
Test.setFixedSearchResults(new List<Id>{ a.Id });
List<List<SObject>> found = [FIND 'anything' IN ALL FIELDS RETURNING Account(Id)];
Worth being clear-eyed about what this proves: it verifies that your code handles results correctly, not that the search query matches anything. SOSL matching itself is not testable in Apex, which is a reason to keep business logic out of the search string.
An integration that creates then looks up
A middleware job that inserts a record and immediately searches for it to get the Id will intermittently find nothing. Use the Id returned by the insert, or upsert on an external Id. Searching for something you just created is almost always a design that should not exist.
A screen flow that creates a record and then uses a lookup
Lookup components in flows and Lightning pages are backed by search. Create a record in one screen and offer it in a lookup on the next, and it may not be there. Pass the record Id forward in a variable instead.
Matching differs too, not just timing
Search tokenises text and applies stemming, so searching for running can match run. It also ignores very common words and applies minimum token lengths. SOQL’s LIKE does none of this — it is literal substring matching.
Two practical consequences. Search will find things you did not literally ask for, which is usually what a human wants and rarely what a process wants. And search may not find a short token or a fragment in the middle of a word, where LIKE '%abc%' would.
The default wildcard behaviour follows from tokenisation: FIND 'acme' matches a token beginning with acme in many cases, but it is not a substring search and treating it as one produces results nobody can explain later.
Choosing between them
- Use SOQL when the record must be found deterministically, when you are filtering on a known field value, when the result drives automation, or when you need the freshest data.
- Use SOSL when a person is typing a phrase, when you need to search several objects at once, or when you want fuzzy matching across text fields.
The rule I would write into a review checklist: if a wrong or missing result changes what the system does rather than what a person sees, it must not come from search.
When search results look wrong
Before assuming indexing lag, rule out the boring causes. They are more common:
- Sharing. Search respects record access. A user not finding a record usually cannot see it, which is a sharing question, not a search one.
- Field not searchable. Not every field type is in the index. Formula fields, for instance, are not searchable in the way text fields are.
- The search term is too short or too common and was dropped during tokenisation.
- Object not in the user’s search scope — search results are personalised by what the user actually uses.
Frequently asked
How long does indexing take?
There is no published figure and it varies with load. Design for “eventually”, not for a number, because any number you measure today is not a contract.
Can I force a record to be re-indexed?
Not directly. Updating the record queues it for reindexing, but there is no supported API to demand immediate indexing.
Does SOSL count against governor limits?
Yes: there is a separate allowance for SOSL queries per transaction, distinct from the SOQL query limit, and each SOSL statement can return a bounded number of rows per object.
Why does global search find a record that SOSL in Apex does not?
Global search applies personalisation and a broader default scope than a SOSL statement with an explicit RETURNING clause. They are not the same query, so do not use one to debug the other.
Does this affect Data Cloud or external objects?
External objects queried through OData are not in the Salesforce search index at all, so their behaviour is different again. Searchability there depends on what the external system supports.
Reference: SOQL and SOSL Reference and Search Records in Salesforce, Salesforce developer and help documentation; Execution Governors and Limits.