HLOOKUP Generator
Search across the first row of a table and return a value from a row below it.
HLOOKUP
Search across the first row of a table and return a value from a row below it.
Cell or value to find in the first row of the table range.
Range whose FIRST ROW contains the values being searched.
Row number counted down from the first row of the table range. Row 1 is the header row itself.
FALSE for an exact match. TRUE needs the header row sorted in ascending order, left to right.
=HLOOKUP(A2, $B$1:$M$5, 3, FALSE)Worked Example
Month names sit across row 1 in B1:M1 and revenue sits in row 3. A2 holds the month you want.
=HLOOKUP(A2, $B$1:$M$5, 3, FALSE)Returns: With A2 = "Mar", Excel finds Mar in the header row (column D) and returns the value in D3 - the third row of the table range.
Checks Before You Paste
- •HLOOKUP only searches the FIRST ROW of the table range, and row_index_num counts down from that same row - so 1 returns the header you just matched, and 2 is the first row of data.
- •Leaving the last argument out (or setting TRUE) switches to approximate match, which requires the header row sorted in ascending order left to right and quietly returns a value from the wrong column when it is not. Use FALSE unless you deliberately want banded lookup.
- •HLOOKUP can never look upward: the return row must sit inside the table range, at or below the header row. If your answer is above the matched row, use INDEX/MATCH or XLOOKUP instead.
How HLOOKUP works
Availability: Every version of Excel, Excel for the web and Google Sheets. XLOOKUP, which supersedes it, needs Excel 2021 or Microsoft 365. Microsoft now lists HLOOKUP as a compatibility function, but it is not deprecated and existing formulas keep working.
HLOOKUP searches across the first row of a range for a value, then returns whatever sits a given number of rows below the cell it matched. It is the sideways twin of VLOOKUP: use it when the labels you are searching for run left to right along a header row - months, quarters, years, size codes, price breaks - and the figures you want run down the columns beneath them.
The two arguments people misjudge are the third and the fourth. row_index_num counts down from the first row of table_array, not from row 1 of the worksheet: if the table starts at B4, an index of 3 returns a value from sheet row 6, and an index of 1 returns the header cell you have just matched. range_lookup controls how the match is made, and its default is the dangerous one. Omit it, or pass TRUE, and Excel does an approximate match that assumes the header row is sorted in ascending order from left to right; on an unsorted header it does not error, it simply returns a value from the wrong column. Pass FALSE for exact matching unless you deliberately want banded lookup on a sorted row of thresholds.
HLOOKUP can only look down. The search row must be the first row of the range, and the answer must be at or below it, so a layout where the figures sit above the labels needs INDEX with MATCH or XLOOKUP instead. Exact matching also compares on type: a header stored as the text "2026" will not match the number 2026, and a trailing space produces #N/A on data that looks identical on screen.
If you have Excel 2021 or Microsoft 365, XLOOKUP does the same job with fewer sharp edges - it defaults to exact matching, takes the return row as a range rather than a counted offset so inserting rows cannot break it, and has a built-in if_not_found argument. HLOOKUP is still worth knowing: it is in every version, and it is what you will find in workbooks other people built.
Syntax
=HLOOKUP(lookup_value, table_array, row_index_num, [range_lookup])| Argument | Required | What it does |
|---|---|---|
lookup_value | Required | The value to find in the first row of table_array. A cell reference, a number, or text in quotes. With exact matching, wildcards * and ? are allowed in text. |
table_array | Required | The block of cells to search. Its first row is the one HLOOKUP looks along, and the returned value must lie inside the same block. Lock it with dollar signs before copying the formula sideways. |
row_index_num | Required | How far down to reach, counting the first row of table_array as 1. It must be at least 1 and no larger than the height of the range, or the result is #VALUE! or #REF! respectively. |
range_lookup | Optional | FALSE for an exact match. TRUE, or leaving the argument out, gives an approximate match that requires the header row sorted ascending left to right and returns the largest header not greater than lookup_value. |
More worked examples
A budget grid with quarters in B1:E1 and three rows beneath: revenue in row 2, costs in row 3, margin in row 4.
=HLOOKUP("Q3", $B$1:$E$4, 4, FALSE)Returns: Finds Q3 in D1 and returns the margin in D4, the fourth row of the range.
Counting from the header row means the margin is index 4, not 3 - the header itself is row 1.
Volume price breaks written across a row: B1:E1 holds 0, 5000, 10000 and 25000, with the rate for each break in B2:E2. An order of 12,500 units in A2.
=HLOOKUP(A2, $B$1:$E$2, 2, TRUE)Returns: Returns the rate in D2, because 10000 is the largest break not above 12,500.
This is the one case where TRUE is correct, and it only works because the breaks are sorted in ascending order along the row.
Checking that a lookup is landing on the column you think it is, before trusting the number it returns.
=HLOOKUP(A2, $B$1:$M$5, 1, FALSE)Returns: Returns the header cell that was matched. If A2 holds "Mar " with a trailing space, this returns #N/A and tells you the problem is the key, not the row index.
Common mistakes
- Counting row_index_num from the worksheet instead of the table
- With a table_array of $B$4:$M$9, an index of 5 returns sheet row 8, not row 5. Everything is measured from the top of the range. Count down from the header row you can see highlighted when you select table_array.
- Leaving range_lookup out
- The default is approximate. On an unsorted header row that returns a value from a neighbouring column with no error at all, so the report is wrong and looks fine. Type FALSE every time unless you are deliberately doing a banded lookup on sorted thresholds.
- Inserting a row breaks the index
- row_index_num is a hard-coded number, so adding a row inside the table silently shifts what it points at, and every formula using that index now returns the wrong measure. Replace the number with =MATCH($A5, $A$1:$A$9, 0) so the index follows a label, or move to XLOOKUP.
- #N/A on headers that look identical
- Exact matching distinguishes the text "2026" from the number 2026, and treats trailing spaces as part of the value. Test with =HLOOKUP(A2, table, 1, FALSE) to see whether the key matches at all, and clean both sides with TRIM or by converting the header row to numbers.
- Needing a value above the header row
- HLOOKUP can never return anything above the row it searched, and it can never search any row but the first of the range. If the labels sit below the figures, or the range would have to start above the labels, use INDEX with MATCH - =INDEX($B$1:$M$1, MATCH(A2, $B$3:$M$3, 0)) - which has no such restriction.
Frequently Asked Questions
Select the range you are using as table_array and count downwards from its top row, which is 1. The first row of actual data is 2. If the range starts in the middle of the sheet, the sheet's own row numbers are irrelevant - only the position within the selection matters.
Almost always because range_lookup was left out or set to TRUE while the header row is not sorted in ascending order left to right. Approximate matching then stops at whatever it considers the closest smaller value and never reports a problem. Add FALSE as the fourth argument.
Only the direction. VLOOKUP searches down the first column of a range and returns a value from a column to the right; HLOOKUP searches along the first row and returns a value from a row below. The arguments are otherwise identical, including the same approximate-match default. If your data is the wrong way round for both, transposing it is often easier than nesting functions.
No. The search row is always the first row of table_array, which is why the fix is usually to change the range rather than the formula. When the labels genuinely sit in the middle of a block, MATCH over that one row combined with INDEX over another is the direct replacement.
Only if every person opening the file has Excel 2021 or Microsoft 365, since XLOOKUP shows #NAME? in older versions. Where that is true it is worth doing: XLOOKUP defaults to an exact match, takes the return row as a real range instead of a counted offset, and handles a missing match without a wrapper.
Related Tools
VLOOKUP Generator
The same lookup for tables whose labels run down a column instead of across a row.
XLOOKUP Generator
The modern replacement, with exact matching by default and no row index to count.
INDEX MATCH Generator
What to use when the answer sits above the row you searched, or the table is awkwardly shaped.
Transpose Excel
Flip a horizontal table into a vertical one so ordinary VLOOKUP or XLOOKUP applies.