Prefer XLOOKUP where you can. HLOOKUP still works in every version of Excel. If everyone who uses the file has Excel 2021 or Microsoft 365, use XLOOKUP instead.

Go to XLOOKUP

Available in:All versions (Works in Excel 2007, every later version, Microsoft 365 and Excel for the web)Examples need Excel 2021 or laterCategory:Lookup & referenceUnder review

HLOOKUP

HORIZ.ZOEKEN in Dutch Excel

Finds a value in the top row of a table and returns the cell a given number of rows below it in the same column.

Difficulty rating 3 of 5. Four arguments look harmless, but the approximate-match default, the row number that breaks when rows move and the one-way direction (key in the top row, answer below it) catch out experienced users.

Last updated: 13 October 2026

See the worked examples

Sheet excerpt: I2 holds the formula
RowABCDEFGHI
1AccountP1P2P3P4P5PeriodAmount
2Revenue-€245,300.00€ -245.300,00-€251,870.50€ -251.870,50-€262,410.00€ -262.410,00-€238,950.25€ -238.950,25-€270,115.00€ -270.115,00P4€99,875.50€ 99.875,50=HLOOKUP(H2, A1:F4, 3, FALSE)=HLOOKUP(H2; A1:F4; 3; FALSE)
3Personnel costs€98,200.00€ 98.200,00€98,200.00€ 98.200,00€101,450.00€ 101.450,00€99,875.50€ 99.875,50€101,450.00€ 101.450,00
4Housing costs€15,400.00€ 15.400,00€15,400.00€ 15.400,00€15,400.00€ 15.400,00€15,400.00€ 15.400,00€16,850.00€ 16.850,00
Formula cell (I2)InputNote: Credit amounts are negative. In a real extract the periods run on to P12.

Result: €99,875.50€ 99.875,50

=HLOOKUP(H2, A1:F4, 3, FALSE)
=HLOOKUP(H2; A1:F4; 3; FALSE)

What HLOOKUP does and when to use it

What it does: it finds a key in the top row of a table and returns the value from the same column, a given number of rows further down, for example the personnel costs of one period.

When to use it: when the periods or codes run across the top and the answer sits in a row below, especially in files that must open in Excel 2019 or older. It is the sideways twin of VLOOKUP. XLOOKUP does the same job without the restrictions, but HLOOKUP is still in many workbooks.

How Excel evaluates it

Excel takes lookup_value and searches the first row of table_array from left to right. With FALSE it stops at the first cell that equals the key; with TRUE it assumes the row is sorted and settles on the largest key that does not exceed lookup_value.

It then counts row_index_num rows down from the top edge of table_array, in that same column, and returns the cell it lands on. If no column qualifies, the result is #N/A.

Watch out: Without FALSE as the fourth argument, HLOOKUP matches approximately, and a missing key quietly returns a neighbouring column instead of #N/A.

Mental model

A ruler held upright on a printed table: you slide it along the top row until it lines up with the key, then read down the ruler to the row number you were told.

Terms used on this page: workbook, array, cell

HLOOKUP(lookup_value, table_array, row_index_num, [range_lookup])
HLOOKUP(lookup_value; table_array; row_index_num; [range_lookup])
Arguments
ArgumentTypeMeaningDefault
lookup_valueRequiredThe value to find in the first row of the table.
table_arrayRequiredThe table; the lookup row must be its first row.
row_index_numRequiredWhich row of the table holds the answer, counting from 1.
range_lookupOptionalFALSE for an exact match; TRUE or omitted for approximate.
  • TRUEApproximate: the largest value not above the lookup value. The key column must run low to high.
  • FALSEExact: only an identical key counts. The order of the table does not matter.
TRUE

Return value:The value in row row_index_num of the matching column, or #N/A

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

Personnel costs of one period from a monthly extract

The client's ledger extract shows the periods across the top (P1 to P5) and one line per account below. You are analysing the movement in personnel costs and need the amount for period P4 on your working paper. You want a cell where you type a period and the amount appears.

Sheet excerpt: I2 holds the formula
RowABCDEFGHIJ
1AccountP1P2P3P4P5PeriodAmount
2Revenue-€245,300.00€ -245.300,00-€251,870.50€ -251.870,50-€262,410.00€ -262.410,00-€238,950.25€ -238.950,25-€270,115.00€ -270.115,00P4€99,875.50€ 99.875,50=HLOOKUP(H2, A1:F4, 3, FALSE)=HLOOKUP(H2; A1:F4; 3; FALSE)
3Personnel costs€98,200.00€ 98.200,00€98,200.00€ 98.200,00€101,450.00€ 101.450,00€99,875.50€ 99.875,50€101,450.00€ 101.450,00
4Housing costs€15,400.00€ 15.400,00€15,400.00€ 15.400,00€15,400.00€ 15.400,00€15,400.00€ 15.400,00€16,850.00€ 16.850,00
5
Formula cell (I2)InputNote: Credit amounts are negative. In a real extract the periods run on to P12.

Formula in I2

=HLOOKUP(H2, A1:F4, 3, FALSE)
=HLOOKUP(H2; A1:F4; 3; FALSE)

Result: €99,875.50€ 99.875,50

Step by step
  1. Excel reads H2 and finds the text P4. That is lookup_value.
  2. FALSE asks for an exact match, so Excel walks along the first row of A1:F4: Account, no; P1, no; P2, no; P3, no; P4, yes. The match is the fifth column of the table, column E of the sheet.
  3. row_index_num is 3, so Excel counts three rows down from the top edge of the table in that column: E1 is row 1 (the key row), E2 row 2, E3 row 3.
  4. It returns the value of E3: 99,875.5099.875,50. Row 3 of the table is the Personnel costs line, because the header row counts as row 1.
  5. Type P2 in H2 and the result becomes 98,200.0098.200,00, the personnel costs of period 2, without touching the formula.

Watch out: Leave out FALSE and type P6 in H2: the sorted period codes let the approximate search settle on P5, and the result is 101,450.00101.450,00, for a period that is not in the extract. With FALSE you get #N/A, which is the honest answer.

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

Why it works

The period code is in the first row of the selected block, which is the only place HLOOKUP searches. The row number is counted inside table_array, not from row 1 of the sheet, so if the extract started in row 10 the 3 would still mean the third row of the block.

More to watch out for

  • If the codes in the top row carry a trailing space ("P4 "), the P4 in H2 never matches and you get #N/A for every period. Clean the header row with TRIM first.

IntermediateExcel 2013 or later (This example needs Excel 2013 or later)

Late-payment interest rate by financial year, copied down a list

The client charges late-payment interest, and the rate changes every financial year. The rates are laid out across the top, one column per year, with a row for standard terms and a row for key accounts.

You recalculate the rate next to each overdue invoice and copy the formula down. Some old invoices belong to a year the table does not cover, and those must say so instead of showing an error.

Sheet excerpt: J2 holds the formula
RowABCDEFGHIJK
1RateFY2022FY2023FY2024FY2025FY2026InvoiceYearRate
2Standard terms5%5%6.5%6,5%8%8%8.5%8,5%7.5%7,5%25-2041FY20248.0%8,0%=IFNA(HLOOKUP(I2, $A$1:$F$3, 2, FALSE), "No rate in table")=IFNA(HLOOKUP(I2; $A$1:$F$3; 2; FALSE); "No rate in table")
3Key accounts4%4%5.5%5,5%7%7%7.5%7,5%6.5%6,5%25-2058FY2021
425-2063FY2026
5
Formula cell (J2)InputNote: The formula is in J2 and copied down to J4.

Formula in J2

=IFNA(HLOOKUP(I2, $A$1:$F$3, 2, FALSE), "No rate in table")
=IFNA(HLOOKUP(I2; $A$1:$F$3; 2; FALSE); "No rate in table")

Result: 8.0%8,0%

Step by step
  1. Excel starts with HLOOKUP. I2 holds FY2024: invoice 25-2041 belongs to financial year 2024.
  2. range_lookup is FALSE, so Excel walks along the first row of $A$1:$F$3: Rate, no; FY2022, no; FY2023, no; FY2024, yes. That is the fourth column of the table, column D.
  3. row_index_num is 2, so Excel returns the cell one row below the key row in column D: D2 holds 0.08, which is 8% on standard terms.
  4. The result is not #N/A, so IFNA passes 0.08 through unchanged.
  5. Copied to J3, the formula reads I3 = FY2021. No header in the top row equals FY2021, so HLOOKUP returns #N/A and IFNA replaces it with the text No rate in table. The $ signs kept the table at $A$1:$F$3, while I2 became I3.
  6. In J4, FY2026 is in the last column, F, so the result is F2: 0.075, which is 7.5%. Without FALSE, a year such as FY2027 would settle on FY2026 and show 7.5% for a year that has no rate; with FALSE it shows the message.

Watch out: Leave out FALSE and an invoice from FY2027 silently gets the FY2026 rate, because the sorted headers let the approximate search settle on the last column. Keep FALSE in every rate lookup on year codes.

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

Why it works

An exact match treats the top row as a list of keys: a year is either in it or it is not. IFNA reacts only to #N/A, which is exactly the signal for a missing year, so other errors such as #REF! stay visible. The $ signs fix the table while the lookup value moves down one row per copy.

More to watch out for

  • IFNA also hides a typing slip. If I2 holds "FY 2024" or "FY2024 ", the message says No rate in table for a year that is in the table. Check the year cells with LEN when the message appears unexpectedly.
  • Insert a new row between the year headers and Standard terms and the 2 now points at the new line instead of Standard terms. Count the rows again after any change to the table.

Variations

=XLOOKUP(I2, $B$1:$F$1, $B$2:$F$2, "No rate in table")
=XLOOKUP(I2; $B$1:$F$1; $B$2:$F$2; "No rate in table")

The XLOOKUP version: the row of years and the row of rates are separate arguments, if_not_found replaces IFNA, and no row number needs counting.

AdvancedExcel 2013 or later (This example needs Excel 2013 or later)

Any line of a budget table, chosen by its label

The client's cost budget has the months across the top and the budget, the actual amounts, the latest forecast and the prior year as rows. In a lookup panel on your working paper you pick a month and a line name to compare with the board minutes. The row number must follow the label, so the formula keeps working when the client adds a line to the table, and an unknown month or line should show a clear message.

Sheet excerpt: H3 holds the formula
RowABCDEFGHI
1LineJanFebMarAprMayMonthMar
2Budget€42,000.00€ 42.000,00€42,000.00€ 42.000,00€45,500.00€ 45.500,00€45,500.00€ 45.500,00€47,000.00€ 47.000,00LineActual
3Actual€41,350.75€ 41.350,75€43,120.50€ 43.120,50€51,840.25€ 51.840,25€44,910.00€ 44.910,00€47,660.80€ 47.660,80Value€51,840.25€ 51.840,25=IFNA(HLOOKUP(H1, $A$1:$F$5, MATCH(H2, $A$1:$A$5, 0), FALSE), "Not in budget table")=IFNA(HLOOKUP(H1; $A$1:$F$5; MATCH(H2; $A$1:$A$5; 0); FALSE); "Not in budget table")
4Latest forecast€42,000.00€ 42.000,00€43,500.00€ 43.500,00€51,000.00€ 51.000,00€45,000.00€ 45.000,00€47,500.00€ 47.500,00
5Prior year€38,900.00€ 38.900,00€39,450.00€ 39.450,00€41,200.00€ 41.200,00€42,780.00€ 42.780,00€43,100.00€ 43.100,00
6
Formula cell (H3)Input

Formula in H3

=IFNA(HLOOKUP(H1, $A$1:$F$5, MATCH(H2, $A$1:$A$5, 0), FALSE), "Not in budget table")
=IFNA(HLOOKUP(H1; $A$1:$F$5; MATCH(H2; $A$1:$A$5; 0); FALSE); "Not in budget table")

Result: €51,840.25€ 51.840,25

Step by step
  1. Excel starts with the innermost function. MATCH looks for H2, Actual, in the label column $A$1:$A$5 with match_type 0: Line, no; Budget, no; Actual, yes. It is the third cell, A3, so MATCH returns 3.
  2. That 3 becomes row_index_num. HLOOKUP now searches the first row of $A$1:$F$5 for H1, Mar, with FALSE: Line, no; Jan, no; Feb, no; Mar, yes. The match is the fourth column of the table, column D.
  3. HLOOKUP returns the third row of that column, D3: 51,840.2551.840,25.
  4. The result is not #N/A, so IFNA passes 51,840.2551.840,25 through unchanged.
  5. Change H2 to Budget: MATCH returns 2 and the result is D2, 45,500.0045.500,00. Change H1 to Jun or H2 to Reforecast: one of the two lookups fails with #N/A, and IFNA replaces it with Not in budget table.
  6. Insert a line called Q1 reforecast between Budget and Actual. Both ranges grow to row 6, MATCH now finds Actual in position 4, and the formula still returns 51,840.2551.840,25. A hard-coded 3 would now return the new line.

Watch out: Leave out the 0 in MATCH and it does an approximate match on the labels, which are not sorted. It can return a plausible but wrong row number, and HLOOKUP then returns the wrong line without any error.

More on this example: why it works, 2 more pitfalls, 2 variations

Why it works

MATCH turns a row label into the row number HLOOKUP needs, so the weakest part of HLOOKUP, the hard-coded row, follows the data instead of the formula. Both ranges start in row 1, which is why the MATCH position and the HLOOKUP row count line up. IFNA only reacts to #N/A, so a broken reference still shows as #REF! instead of being disguised as an unknown line.

More to watch out for

  • If the label range starts in row 2 while the table starts in row 1, every result is one row off. Both ranges must start in the same row.
  • One message covers two different failures. When the panel shows Not in budget table you do not know whether the month or the line was wrong, so check both cells before concluding that a figure is missing.

Variations

=IFNA(XLOOKUP(H1, $B$1:$F$1, XLOOKUP(H2, $A$2:$A$5, $B$2:$F$5)), "Not in budget table")
=IFNA(XLOOKUP(H1; $B$1:$F$1; XLOOKUP(H2; $A$2:$A$5; $B$2:$F$5)); "Not in budget table")

The XLOOKUP version: the inner XLOOKUP picks the row, the outer one the column, and IFNA still catches either failure.

=IFNA(INDEX($B$2:$F$5, MATCH(H2, $A$2:$A$5, 0), MATCH(H1, $B$1:$F$1, 0)), "Not in budget table")
=IFNA(INDEX($B$2:$F$5; MATCH(H2; $A$2:$A$5; 0); MATCH(H1; $B$1:$F$1; 0)); "Not in budget table")

INDEX with two MATCH functions works in every version and can also return a line above the key row.

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

Several cost lines of one quarter in a single HLOOKUP

The client's cost report has the quarters across the top and the cost lines below: wages, social charges, pension contributions, rent and travel.

For your analytical review you need the total personnel cost (the first three lines) for a quarter you pick. Three separate HLOOKUPs work, but row_index_num also accepts a list of row numbers, so one HLOOKUP can fetch all three lines at once.

Sheet excerpt: H2 holds the formula
RowABCDEFGHI
1Cost lineQ1Q2Q3Q4QuarterQ3
2Wages€312,400.00€ 312.400,00€318,750.00€ 318.750,00€321,900.00€ 321.900,00€329,850.00€ 329.850,00Personnel costs€434,565.00€ 434.565,00=LET( quarter, H1, lines, {2,3,4}, IFNA(SUM(HLOOKUP(quarter, $A$1:$E$6, lines, FALSE)), "Quarter not in table") )=LET( quarter; H1; lines; {2\3\4}; IFNA(SUM(HLOOKUP(quarter; $A$1:$E$6; lines; FALSE)); "Quarter not in table") )
3Social charges€78,100.00€ 78.100,00€79,687.50€ 79.687,50€80,475.00€ 80.475,00€82,462.50€ 82.462,50
4Pension contributions€31,240.00€ 31.240,00€31,875.00€ 31.875,00€32,190.00€ 32.190,00€32,985.00€ 32.985,00
5Rent€45,600.00€ 45.600,00€45,600.00€ 45.600,00€45,600.00€ 45.600,00€45,600.00€ 45.600,00
6Travel€12,480.00€ 12.480,00€15,920.00€ 15.920,00€11,260.00€ 11.260,00€14,190.00€ 14.190,00
7
Formula cell (H2)InputNote: The first three lines of the table (rows 2 to 4) make up the personnel costs.

Formula in H2

=LET(
  quarter, H1,
  lines, {2,3,4},
  IFNA(SUM(HLOOKUP(quarter, $A$1:$E$6, lines, FALSE)), "Quarter not in table")
)
=LET(
  quarter; H1;
  lines; {2\3\4};
  IFNA(SUM(HLOOKUP(quarter; $A$1:$E$6; lines; FALSE)); "Quarter not in table")
)

Result: €434,565.00€ 434.565,00

Step by step
  1. LET names the inputs: quarter becomes Q3 (from H1) and lines becomes the list 2, 3, 4.
  2. HLOOKUP searches the first row of $A$1:$E$6 for Q3 with FALSE: Cost line, no; Q1, no; Q2, no; Q3, yes. The match is the fourth column of the table, column D.
  3. Because row_index_num is a list, Excel reads one row per number in that same column: row 2 gives D2 = 321,900321.900; row 3 gives D3 = 80,47580.475; row 4 gives D4 = 32,19032.190. HLOOKUP hands back all three as an array.
  4. SUM adds the array: 321,900321.900 + 80,47580.475 + 32,19032.190 = 434,565434.565.
  5. There is no #N/A, so IFNA passes 434,565434.565 through unchanged.
  6. Type Q4 in H1 and the three values are 329,850329.850 + 82,462.5082.462,50 + 32,98532.985 = 445,297.50445.297,50. Type Q5: every one of the three lookups fails with #N/A, SUM passes the error on, and IFNA shows Quarter not in table.

Watch out: A frequent mistake: leaving out FALSE. The sorted quarter codes let an approximate search settle on Q4 when you type Q5, so the formula returns the Q4 total for a quarter that is not in the report, with no error.

More on this example: why it works, 2 more pitfalls, 2 variations

Why it works

Because row_index_num is a list, HLOOKUP returns one value per row number from the matching column, and SUM adds them. Microsoft documents only a single number for row_index_num; a list works but is not described on its help page.

Do not use this construction when:

  • the block is fixed and you can sum it directly (SUM over the three cells of the quarter)
  • lines are added or reordered often, because the row numbers are positions that do not follow the labels
  • the reader must be able to see at a glance which lines are included

The alternatives are XLOOKUP, which returns the whole column of the quarter and lets you sum the lines you want, and INDEX with MATCH, which does the same in every version of Excel.

More to watch out for

  • Insert a line above Pension contributions and the list 2, 3, 4 still points at the same positions, so the new line is counted and Pension contributions drops out of the total. The result still looks like a plausible amount. Recheck the list after any change to the table.
  • LET needs Excel 2021 or Microsoft 365. In older versions, use the INDEX form, which does not depend on LET or on array handling.

Variations

=IFNA(SUM(INDEX($B$2:$E$4, 0, MATCH(H1, $B$1:$E$1, 0))), "Quarter not in table")
=IFNA(SUM(INDEX($B$2:$E$4; 0; MATCH(H1; $B$1:$E$1; 0))); "Quarter not in table")

INDEX with row_num 0 returns the whole column of the quarter for the three lines, and MATCH finds the column. It works in every version and the included lines are visible in the range B2:E4.

=IFNA(SUM(XLOOKUP(H1, $B$1:$E$1, $B$2:$E$4)), "Quarter not in table")
=IFNA(SUM(XLOOKUP(H1; $B$1:$E$1; $B$2:$E$4)); "Quarter not in table")

XLOOKUP with a row of keys and a block of three rows returns the three values of the matching column, so no row numbers are needed. It requires Excel 2021 or later.

Mode explorer

Type a lookup value, pick the row to return and the range_lookup setting, and see which column HLOOKUP takes it from.

Sheet excerpt: B5 holds the formula
RowABCDEF
1Band12345
2Days overdue from00303060609090120120
3Provision (%)0.50,522101025255050
4
5Result1010=HLOOKUP("x", $B$2:$F$3, 2, TRUE)=HLOOKUP("x"; $B$2:$F$3; 2; TRUE)
Formula cell (B5)
  • Formula cell
  • Where the search lands
  • What it returns

=HLOOKUP(74, $B$2:$F$3, 2, TRUE)
=HLOOKUP(74; $B$2:$F$3; 2; TRUE)

Result: 1010

74 is not in the top row. TRUE takes the column of 60, the largest value not above 74: provision 10%.

=HLOOKUP(74, $B$2:$F$3, 2, FALSE)
=HLOOKUP(74; $B$2:$F$3; 2; FALSE)

Result: #N/A

74 is not in the table, so an exact search finds nothing and gives #N/A.

=HLOOKUP(74, $B$2:$F$3, 1, TRUE)
=HLOOKUP(74; $B$2:$F$3; 1; TRUE)

Result: 6060

=HLOOKUP(74, $B$2:$F$3, 1, FALSE)
=HLOOKUP(74; $B$2:$F$3; 1; FALSE)

Result: #N/A

Common mistakes

Common mistakes
SymptomCauseFix
A neighbouring value instead of #N/AA missing period code returns the value of a neighbouring column instead of #N/A, and nobody notices.Add FALSE (or 0) as the last argument for every lookup on codes, periods or names.
The wrong line after inserting a rowA row is inserted inside the table and the formula now returns the wrong line, because row_index_num is still 3.Let MATCH find the row number from the row label, as in the Advanced example, or switch to XLOOKUP, which points at the answer row directly.
The answer row cannot be reachedThe answer row is above the key row, and no row_index_num reaches it. A related slip is typing the sheet row number instead of the row number inside the table.Use INDEX with MATCH, or XLOOKUP, when the answer sits above the keys. Count the rows from the top of table_array, so the key row itself is 1.
Later copies miss keys in the top rowThe formula is copied down or across without $, so table_array shifts with every copy and the later copies miss keys in the top row of the table.Write the table as $A$1:$F$5, or turn it into a Table and use Table1[#All], which includes the header row where the keys sit.