Available in:Excel 2021 or later (Works in Excel 2021, every later version, Microsoft 365 and Excel for the web)Examples need Excel 2024 or laterCategory:Lookup & reference

XMATCH

X.VERGELIJKEN in Dutch Excel

Finds a value in a row or column and returns its position as a number, with options for nearest matches, wildcards, regular expressions and searching from the end.

Difficulty rating 3 of 5. It returns a position rather than a value, so it is almost always combined with INDEX or another function, and its match and search modes need to be learned.

Last updated: 13 October 2026

See the worked examples

Sheet excerpt: F2 holds the formula
RowABCDEF
1AccountDescriptionBalanceAccountPosition
211001100Bank€84,250.00€ 84.250,004500450055=XMATCH(E2, A2:A7)=XMATCH(E2; A2:A7)
313001300Receivables€312,400.00€ 312.400,00
416001600Payables-€156,800.00€ -156.800,00
540004000Payroll costs€428,500.00€ 428.500,00
645004500Premises costs€96,300.00€ 96.300,00
780008000Revenue-€1,245,000.00€ -1.245.000,00
Formula cell (F2)Input

Result: 5

=XMATCH(E2, A2:A7)
=XMATCH(E2; A2:A7)

What XMATCH does and when to use it

What it does: it finds a value in a row or column and returns its position as a number, for example the row of an account on the trial balance.

When to use it: when you need the position rather than the value: to feed INDEX, to pick a column by its header, to test whether something is in a list or to find the last occurrence of a key. If you only need the value from a neighboring column, XLOOKUP does it in one step.

Three main gains:

  • Exact by default: MATCH without a third argument looks for an approximate match, XMATCH looks for an exact one.
  • Search from the end: search_mode -1 returns the last occurrence of a key, which MATCH cannot do.
  • Nearest match without sorting: match_mode -1 or 1 finds the next smaller or larger value in unsorted data.

How Excel evaluates it

Excel reads lookup_value and then walks through lookup_array in the direction search_mode sets, top to bottom unless told otherwise. For each cell it applies the rule of match_mode:

  • exact equality (ignoring upper and lower case in modes 0, -1, 1 and 2)
  • a match with a wildcard pattern (mode 2) or a regular expression (mode 3, Microsoft 365 only; case sensitive unless the pattern starts with (?i))
  • the closest value below or above, which it remembers

As soon as a match is settled, it returns the position counted from the first cell of lookup_array, whichever direction it searched in. If nothing qualifies, it returns #N/A.

Watch out: XMATCH returns a position, not a value: 4 means the fourth cell of lookup_array, not what is in it.

Mental model

A cloakroom ticket: XMATCH hands you the number of the hook, not the coat. INDEX is the attendant who fetches whatever hangs on that hook.

Terms used on this page: regular expression, wildcard, array, argument, cell

XMATCH(lookup_value, lookup_array, [match_mode], [search_mode])
XMATCH(lookup_value; lookup_array; [match_mode]; [search_mode])
Arguments
ArgumentTypeMeaningDefault
lookup_valueRequiredThe value whose position you want.
lookup_arrayRequiredThe single row or column to search. Positions count from its first cell.
match_modeOptionalExact (default), next smaller, next larger, wildcard or regex.
  • 0Only an exact match counts (the default)
  • -1Exact match, or else the next smaller value
  • 1Exact match, or else the next larger value
  • 2Wildcards count: * is any characters, ? is one
  • 3The lookup value is a regular expression pattern; case sensitive
0
search_modeOptionalDirection of the search, or binary search on sorted data.
  • 1Search from the first row down (the default)
  • -1Search from the last row up, for the latest entry
1

Return value:A number: the position of lookup_value in lookup_array

Formulas use commas (,) between arguments, as in English Excel. Formulas use semicolons (;) between arguments, as in Dutch Excel. Formulas use commas (,) between arguments, as in English Excel; Dutch Excel uses semicolons (;).

Worked examples

Beginner

Find an account's position on the trial balance

You received the trial balance of Veldhuis Installatie BV as a list, sorted by account number. During the reconciliation you keep asking where a given account sits in that list. You want a cell where you type an account number and see its position, without counting rows by hand.

Sheet excerpt: F2 holds the formula
RowABCDEFG
1AccountDescriptionBalanceAccountPosition
211001100Bank€84,250.00€ 84.250,004500450055=XMATCH(E2, A2:A7)=XMATCH(E2; A2:A7)
313001300Receivables€312,400.00€ 312.400,00
416001600Payables-€156,800.00€ -156.800,00
540004000Payroll costs€428,500.00€ 428.500,00
645004500Premises costs€96,300.00€ 96.300,00
780008000Revenue-€1,245,000.00€ -1.245.000,00
8
Formula cell (F2)Input

Formula in F2

=XMATCH(E2, A2:A7)
=XMATCH(E2; A2:A7)

Result: 55

Step by step
  1. Excel reads E2 and finds the number 4500. That is the lookup_value.
  2. It walks through A2:A7 from the top: 1100, no; 1300, no; 1600, no; 4000, no; 4500, yes. The match is in A6.
  3. A6 is the fifth cell of A2:A7, so XMATCH returns 5. Not the balance of 96,30096.300 and not sheet row 6: the position inside the range.
  4. Type 8000 and the result is 6. Type an account that is not on the trial balance, such as 4200, and the result is #N/A: a quick sign that the account is missing from the client's list.

Watch out: If the export stores account numbers as text ("4500") while you type the number 4500, there is no match and you get #N/A. Check that both sides are the same type before you conclude that an account is missing.

More on this example: why it works

Why it works

With only two arguments, XMATCH uses exact match and searches from the top, and it reports where it found the value instead of what is next to it. Account numbers must match exactly, so a missing account shows up at once as #N/A.

Intermediate

Find the first journal line for a relation code

The XAF export of Veldhuis Installatie BV has one text key per journal line, built as journal, entry number, relation and VAT rate, such as VRK|25-0412|D1001|21%. You want to know on which row the first line for a relation code appears, as a starting point for a sample.

Some keys were typed in lower case, and you copy the formula down for several relations.

Sheet excerpt: E2 holds the formula
RowABCDEF
1Export keyAmountRelationFirst row
2VRK|25-0388|D1002|21%€4,235.00€ 4.235,00D100133=XMATCH("*" & D2 & "*", $A$2:$A$7, 2)=XMATCH("*" & D2 & "*"; $A$2:$A$7; 2)
3INK|25-0102|C2001|21%€1,815.00€ 1.815,00C2003
4vrk|25-0412|d1001|21%€2,420.00€ 2.420,00D1009
5VRK|25-0431|D1001|9%€654.50€ 654,50
6BNK|25-0517|C2003|0%€3,000.00€ 3.000,00
7MEM|25-0520|D1003|21%€980.00€ 980,00
8
Formula cell (E2)Input

Formula in E2

=XMATCH("*" & D2 & "*", $A$2:$A$7, 2)
=XMATCH("*" & D2 & "*"; $A$2:$A$7; 2)

Result: 33

Step by step
  1. Excel builds lookup_value first: "*" & D2 & "*" joins an asterisk, D1001 and another asterisk into the pattern *D1001*, meaning 'anything, then D1001, then anything'.
  2. match_mode 2 tells XMATCH to treat * as a wildcard. It checks A2:A7 from the top: VRK|25-0388|D1002|21% no, INK|25-0102|C2001|21% no, vrk|25-0412|d1001|21% yes, because in mode 2 XMATCH does not care about upper or lower case.
  3. The match is the third cell, so the result is 3. VRK|25-0431|D1001|9% in the fourth cell also fits, but the search has already stopped.
  4. Without the 2, match_mode is 0 (exact), and XMATCH looks for the literal text *D1001*, asterisks included. No key contains that, so the result would be #N/A.
  5. Copied to E3, the pattern becomes *C2003* and the result is 5. E4 builds *D1009* and returns #N/A: that relation has no lines in the export. The $ signs keep the list at A2:A7 in every copy.

Watch out: *D100* would match D1001, D1002 and D1003 alike. A pattern with asterisks on both sides matches anywhere in the key, so a short code finds the wrong relation.

More on this example: why it works, 1 more pitfall, 1 variation

Why it works

match_mode 2 switches on the wildcard rules, so the asterisks around the relation code accept any journal and entry number before it and any VAT rate after it. In mode 2 the comparison ignores case, which is why the lower-case key still counts.

More to watch out for

  • If case matters (a lower-case key may point to a manual correction), XMATCH cannot tell the difference in modes 0 to 2 (mode 3 is case sensitive when you anchor the pattern with ^ and $, but then special characters such as . and | need escaping). Compare with EXACT instead.

Variations

=XMATCH("*|" & D2 & "|*", $A$2:$A$7, 2)
=XMATCH("*|" & D2 & "|*"; $A$2:$A$7; 2)

Anchor the pattern to the separators so the relation code can only match the relation segment. Prefer this when codes share characters.

Advanced

Latest invoice and date per relation before year end

For the cut-off test you have the sales journal of Veldhuis Installatie BV for the last quarter of 2025, oldest line at the top. You type a customer code and want the date and invoice number of its last invoice before 31 December 2025. The same customer appears several times, so the search has to start from the bottom.

Sheet excerpt: F2 holds the formula
RowABCDEFGH
1DateRelationInvoice no.RelationD1002
26 Oct 2025D100125-0301Last invoice2 Dec 2025=INDEX(A2:C8, XMATCH(F1, B2:B8, 0, -1), {1,3})=INDEX(A2:C8; XMATCH(F1; B2:B8; 0; -1); {1\3})25-0377
321 Oct 2025D100225-0318
44 Nov 2025D100125-0342
518 Nov 2025D100325-0359
62 Dec 2025D100225-0377
715 Dec 2025D100125-0391
829 Dec 2025D100325-0410
9
Formula cell (F2)InputSpilled result

Formula in F2

=INDEX(A2:C8, XMATCH(F1, B2:B8, 0, -1), {1,3})
=INDEX(A2:C8; XMATCH(F1; B2:B8; 0; -1); {1\3})

Result: 2 Dec 2025 | 25-03772 Dec 2025 | 25-0377

Step by step
  1. Excel evaluates XMATCH first. It looks for F1, D1002, in B2:B8 with match_mode 0 (exact) and search_mode -1 (from last to first).
  2. It starts at B8: D1003, no. B7: D1001, no. B6: D1002, yes. It stops there; the earlier D1002 in B3 is never reached.
  3. The position is counted from the top of B2:B8, whatever the search direction: B6 is the fifth cell, so XMATCH returns 5.
  4. INDEX evaluates INDEX(A2:C8, 5, {1,3}). Row 5 of the block is sheet row 6. column_num is an array of two numbers, so INDEX returns two cells: column 1, A6, which is 2025-12-02, and column 3, C6, which is 25-0377.
  5. The two values spill into F2 and G2. Format F2 as a date, otherwise it shows the serial number 45993.
  6. Change F1 to D1003 and the search from the end stops at B8, position 7: 2025-12-29 and 25-0410.

Watch out: If a back-dated invoice from October is posted at the bottom of the journal, the last occurrence is no longer the latest date, and the formula returns that October invoice without any error. Keep the list sorted by date, or use MAXIFS on the dates (MAXIFS needs Excel 2019 or later).

More on this example: why it works, 1 more pitfall, 3 variations

Why it works

search_mode -1 changes where the search starts, not how positions are counted, so the number it returns is still a valid row for INDEX. Because the journal is in date order, the last occurrence is the most recent invoice. The array constant {1,3} lets one INDEX return two non-adjacent columns.

More to watch out for

  • Leave out the -1 and XMATCH searches from the top, finding D1002 in B3 at position 2: 2025-10-21 and 25-0318. A real invoice, the wrong one for a cut-off test.

Variations

=XLOOKUP(F1, B2:B8, C2:C8, "No invoice", 0, -1)
=XLOOKUP(F1; B2:B8; C2:C8; "No invoice"; 0; -1)

XLOOKUP with the same search_mode returns only the invoice number, with a message for unknown relations. Prefer it when one column is enough.

=INDEX(A2:C8, XMATCH(F1, B2:B8, 0, -1), 0)
=INDEX(A2:C8; XMATCH(F1; B2:B8; 0; -1); 0)

A 0 for column_num returns the whole row: date, relation and invoice number.

=XMATCH("^D100[12]$", B2:B8, 3, -1)
=XMATCH("^D100[12]$"; B2:B8; 3; -1)

Match a group of relation codes by pattern (Microsoft 365). ^D100[12]$ fits D1001 and D1002 only: ^ and $ pin the pattern to the whole code, and [12] is one digit, 1 or 2. Searching from the bottom, B8 (D1003) does not fit and B7 (D1001) does, so XMATCH returns 6.

MasterExcel 2024 or later (This example needs Excel 2024 or later)

The entry where cumulative differences reach performance materiality

During the receivables work you list the differences found per journal entry for Veldhuis Installatie BV, all as overstatements in euros. You want to know at which entry the running total first reaches performance materiality, without a helper column in the working paper. The formula must keep working when the threshold in E1 changes.

Sheet excerpt: E2 holds the formula
RowABCDEF
1Entry no.DifferencePerformance materiality4000040000
225-0108€8,500.00€ 8.500,00Reached at entry25-0287=LET( running, SCAN(0, B2:B9, LAMBDA(total, x, total + x)), pos, XMATCH(E1, running, 1), INDEX(A2:A9, pos) )=LET( running; SCAN(0; B2:B9; LAMBDA(total; x; total + x)); pos; XMATCH(E1; running; 1); INDEX(A2:A9; pos) )
325-0155€12,300.00€ 12.300,00
425-0201€4,800.00€ 4.800,00
525-0233€9,600.00€ 9.600,00
625-0287€11,200.00€ 11.200,00
725-0310€6,400.00€ 6.400,00
825-0344€3,900.00€ 3.900,00
925-0371€7,500.00€ 7.500,00
10
Formula cell (E2)Input

Formula in E2

=LET(
  running, SCAN(0, B2:B9, LAMBDA(total, x, total + x)),
  pos, XMATCH(E1, running, 1),
  INDEX(A2:A9, pos)
)
=LET(
  running; SCAN(0; B2:B9; LAMBDA(total; x; total + x));
  pos; XMATCH(E1; running; 1);
  INDEX(A2:A9; pos)
)

Result: 25-0287

Step by step
  1. SCAN starts at 0, adds each difference in turn and keeps every intermediate total: 8500; + 12300 = 20800; + 4800 = 25600; + 9600 = 35200; + 11200 = 46400; + 6400 = 52800; + 3900 = 56700; + 7500 = 64200. LET names that array running.
  2. XMATCH looks for E1, 40000, in running with match_mode 1: exact match, or else the next larger value. No total is exactly 40000.
  3. The smallest total above 40000 is 46400, the fifth element, so pos is 5. The array came from SCAN, not from a range on the sheet, and XMATCH searches it all the same.
  4. INDEX(A2:A9, 5) returns A6: 25-0287. Because every difference here is positive, running totals only go up, so the next larger total is also the first entry at or above the threshold.
  5. Set E1 to 64200 and there is an exact match at position 8: 25-0371. Set it to 70000 and XMATCH returns #N/A, because the differences never reach that level.

Watch out: The mirror-image mistake is match_mode -1. It returns 35200 at position 4, entry 25-0233: the last entry before the threshold was reached. The answer is off by exactly one entry and looks entirely reasonable.

More on this example: why it works, 1 more pitfall, 1 variation

Why it works

XMATCH accepts any array as lookup_array, so the running totals never need to exist on the sheet. match_mode 1 finds the first value that reaches the threshold in an ascending series without requiring the data to be sorted. LET names each step, so the formula reads as 'running total, position, entry'.

Do not use this construction when the differences can be negative (understatements netting against overstatements): the running total can dip, and the next larger value is then not necessarily the first time the threshold was reached.

The alternative is XMATCH(TRUE, running >= E1), shown below, which finds the first entry where the condition holds in any order, at the cost of an extra comparison over the whole array.

More to watch out for

  • Adding search_mode 2 for speed looks safe because the running totals are sorted, but the gain only shows on thousands of rows, and it breaks silently the day a negative difference appears.

Variations

=INDEX(A2:A9, XMATCH(TRUE, SCAN(0, B2:B9, LAMBDA(total, x, total + x)) >= E1))
=INDEX(A2:A9; XMATCH(TRUE; SCAN(0; B2:B9; LAMBDA(total; x; total + x)) >= E1))

Searches for the first TRUE in a column of comparisons. Works even when running totals dip; prefer it when negative differences are possible.

Mode explorer

Type a lookup value, choose where to look, pick a match mode and a search mode, and see which position XMATCH returns.

Sheet excerpt: E2 holds the formula
RowABCDE
1ReferenceAmountPosition
2CN-0412-€1,200.00€ -1.200,0044=XMATCH("x", $B$2:$B$8, 0, 1)=XMATCH("x"; $B$2:$B$8; 0; 1)
3INV-1001€450.00€ 450,00
4INV-1002€450.00€ 450,00
5INV-1003€1,800.00€ 1.800,00
6INV-1004€3,250.00€ 3.250,00
7INV-1005€7,500.00€ 7.500,00
8INV-1006€12,000.00€ 12.000,00
Formula cell (E2)
  • Formula cell
  • Where the search lands

=XMATCH(1800, $B$2:$B$8, 0, 1)
=XMATCH(1800; $B$2:$B$8; 0; 1)

Result: 44

=XMATCH(1800, $B$2:$B$8, 0, -1)
=XMATCH(1800; $B$2:$B$8; 0; -1)

Result: 44

=XMATCH(1800, $B$2:$B$8, -1, 1)
=XMATCH(1800; $B$2:$B$8; -1; 1)

Result: 44

=XMATCH(1800, $B$2:$B$8, -1, -1)
=XMATCH(1800; $B$2:$B$8; -1; -1)

Result: 44

=XMATCH(1800, $B$2:$B$8, 1, 1)
=XMATCH(1800; $B$2:$B$8; 1; 1)

Result: 44

=XMATCH(1800, $B$2:$B$8, 1, -1)
=XMATCH(1800; $B$2:$B$8; 1; -1)

Result: 44

=XMATCH(1800, $B$2:$B$8, 2, 1)
=XMATCH(1800; $B$2:$B$8; 2; 1)

Result: 44

=XMATCH(1800, $B$2:$B$8, 2, -1)
=XMATCH(1800; $B$2:$B$8; 2; -1)

Result: 44

=XMATCH(1800, $B$2:$B$8, 3, 1)
=XMATCH(1800; $B$2:$B$8; 3; 1)

Result: 44

=XMATCH(1800, $B$2:$B$8, 3, -1)
=XMATCH(1800; $B$2:$B$8; 3; -1)

Result: 44

=XMATCH(1800, $A$2:$A$8, 0, 1)
=XMATCH(1800; $A$2:$A$8; 0; 1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, 0, -1)
=XMATCH(1800; $A$2:$A$8; 0; -1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, -1, 1)
=XMATCH(1800; $A$2:$A$8; -1; 1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, -1, -1)
=XMATCH(1800; $A$2:$A$8; -1; -1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, 1, 1)
=XMATCH(1800; $A$2:$A$8; 1; 1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, 1, -1)
=XMATCH(1800; $A$2:$A$8; 1; -1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, 2, 1)
=XMATCH(1800; $A$2:$A$8; 2; 1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, 2, -1)
=XMATCH(1800; $A$2:$A$8; 2; -1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, 3, 1)
=XMATCH(1800; $A$2:$A$8; 3; 1)

Result: #N/A

=XMATCH(1800, $A$2:$A$8, 3, -1)
=XMATCH(1800; $A$2:$A$8; 3; -1)

Result: #N/A

Common mistakes

Common mistakes
SymptomCauseFix
A small whole number instead of a name or amountYou expected a name or an amount and got a small whole number.Wrap it in INDEX on the column that holds the answer, or use XLOOKUP if a value is all you need.
INDEX returns the value from the row belowINDEX with XMATCH returns the value from the row below the right one.lookup_array includes the header and the INDEX range does not. Start both ranges on the same row.
Different results after replacing MATCH, mode -1You replaced MATCH(x, range, -1) with XMATCH(x, range, -1) and the results changed.The same code means opposite things: MATCH -1 finds the smallest value that is at least x, XMATCH -1 finds the largest value that is at most x. For MATCH -1 behavior, use XMATCH match_mode 1.
Plausible but wrong positions with search_mode 2search_mode 2 returns positions that look reasonable but are wrong.Binary search assumes the list is sorted ascending and does not check. Sort the data, or leave search_mode out.
#VALUE! with match_mode 3match_mode 3 (regular expression) works only in Microsoft 365. In Excel 2021 or 2024 the same formula returns #VALUE!.Use match_mode 2 with wildcards where * and ? are enough, or open the file in Microsoft 365. Check who else opens the workbook before you rely on mode 3.

Used in combinations

  • Invoices missing from the ledger with FILTER, ISNA and XMATCHList the invoices on the receivables specification that are not in the general ledger: XMATCH looks each one up, ISNA turns every miss into TRUE, and FILTER keeps those rows, with a message when nothing is missing.Excel 2021 or later (This combination needs Excel 2021 or later)
  • Duplicate invoice numbers across two exportsStack two monthly purchase exports into one list and keep each invoice number that occurs more than once, using VSTACK, UNIQUE, XMATCH, ISNA and FILTER.Excel 2024 or later (This combination needs Excel 2024 or later)
  • Gap test on invoice numbers with FILTER, SEQUENCE and XMATCHFind the missing numbers in a series of sales invoices: SEQUENCE builds every number from the lowest to the highest, XMATCH looks each one up in the ledger, ISNA marks the misses and FILTER lists them, with a message when there are no gaps.Excel 2021 or later (This combination needs Excel 2021 or later)
  • Two-way lookup with one XLOOKUP and TOCOLFind the value where a row label and a column header meet, by turning both conditions into one grid of 0s and 1s and flattening it with TOCOL.Excel 2024 or later (This combination needs Excel 2024 or later)