[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-\u002Fblog\u002Fplss-power-bi-excel-google-sheets-oil-gas-analytics":3},{"id":4,"title":5,"body":6,"cover":109,"date":564,"description":565,"draft":566,"extension":567,"meta":568,"navigation":587,"path":588,"seo":589,"stem":590,"tags":591,"__hash__":602},"blog\u002Fblog\u002Fplss-power-bi-excel-google-sheets-oil-gas-analytics.md","Convert Oklahoma Section, Township, Range to Lat\u002FLong for Power BI, Excel, and Google Sheets",{"type":7,"value":8,"toc":556},"minimark",[9,26,29,34,43,46,92,103,258,273,281,285,292,300,311,318,321,325,333,336,342,361,368,372,375,378,381,455,474,478,484,487,519,522,535,539,546,552],[10,11,12,13,17,18,21,22,25],"p",{},"Your Oklahoma well table has a column called ",[14,15,16],"code",{},"legal_description",". Every row reads something like ",[14,19,20],{},"T11N R8W Sec 14 NE¼, Indian Meridian"," — a string the Power BI map visual cannot plot, the Excel chart wizard cannot graph, and the Google Sheets ",[14,23,24],{},"MAP"," chart will not accept. There is no built-in geocoder anywhere in the Microsoft or Google data analytics stack that understands the Public Land Survey System.",[10,27,28],{},"This is the gap. You have hundreds — sometimes thousands — of SCOOP and STACK well records keyed by PLSS legal description, and you need them as latitude\u002Flongitude before any map, distance calculation, or spatial join will work. The fix is the same in all three tools: call a PLSS API once per row, expand the response, and keep going. Here is the workflow for each.",[30,31,33],"h2",{"id":32},"power-bi-convert-plss-to-latlong-in-power-query","Power BI: convert PLSS to lat\u002Flong in Power Query",[10,35,36,37,42],{},"Power BI is where most production O&G dashboards live, and Power Query is the right place to do the conversion. Adding a custom column that calls the Township America ",[38,39,41],"a",{"href":40},"\u002Fapi","Search API"," gives you a clean, refreshable column of latitude\u002Flongitude that travels with the rest of the model.",[10,44,45],{},"Start with a table that has your PLSS legal descriptions in one column:",[47,48,49,65],"table",{},[50,51,52],"thead",{},[53,54,55,61],"tr",{},[56,57,58],"th",{},[14,59,60],{},"well_id",[56,62,63],{},[14,64,16],{},[66,67,68,76,84],"tbody",{},[53,69,70,74],{},[71,72,73],"td",{},"OK-00187",[71,75,20],{},[53,77,78,81],{},[71,79,80],{},"OK-00188",[71,82,83],{},"T12N R8W Sec 22 SW¼, Indian Meridian",[53,85,86,89],{},[71,87,88],{},"OK-00189",[71,90,91],{},"T10N R7W Sec 6 NW¼NW¼, Indian Meridian",[10,93,94,95,98,99,102],{},"Add a Power Query step that calls the API for each row. Read the key from a Power BI parameter named ",[14,96,97],{},"TA_API_KEY"," so it never lands in your ",[14,100,101],{},".pbix",":",[104,105,110],"pre",{"className":106,"code":107,"language":108,"meta":109,"style":109},"language-m shiki shiki-themes material-theme-lighter github-light github-dark","let\n    apiKey = TA_API_KEY,\n    Source = #\"Wells\",\n    Geocoded = Table.AddColumn(Source, \"geo\", each\n        let\n            body = Json.FromValue([description = [legal_description]]),\n            response = Web.Contents(\n                \"https:\u002F\u002Fdeveloper.townshipamerica.com\u002Fv1\u002Fsearch\",\n                [\n                    Headers = [\n                        #\"Content-Type\" = \"application\u002Fjson\",\n                        #\"x-api-key\" = apiKey\n                    ],\n                    Content = body\n                ]\n            ),\n            parsed = Json.Document(response),\n            point = parsed[features]{0}[geometry][coordinates]\n        in\n            [longitude = point{0}, latitude = point{1}]\n    ),\n    Expanded = Table.ExpandRecordColumn(Geocoded, \"geo\", {\"latitude\", \"longitude\"})\nin\n    Expanded\n","m","",[14,111,112,120,126,132,138,144,150,156,162,168,174,180,186,192,198,204,210,216,222,228,234,240,246,252],{"__ignoreMap":109},[113,114,117],"span",{"class":115,"line":116},"line",1,[113,118,119],{},"let\n",[113,121,123],{"class":115,"line":122},2,[113,124,125],{},"    apiKey = TA_API_KEY,\n",[113,127,129],{"class":115,"line":128},3,[113,130,131],{},"    Source = #\"Wells\",\n",[113,133,135],{"class":115,"line":134},4,[113,136,137],{},"    Geocoded = Table.AddColumn(Source, \"geo\", each\n",[113,139,141],{"class":115,"line":140},5,[113,142,143],{},"        let\n",[113,145,147],{"class":115,"line":146},6,[113,148,149],{},"            body = Json.FromValue([description = [legal_description]]),\n",[113,151,153],{"class":115,"line":152},7,[113,154,155],{},"            response = Web.Contents(\n",[113,157,159],{"class":115,"line":158},8,[113,160,161],{},"                \"https:\u002F\u002Fdeveloper.townshipamerica.com\u002Fv1\u002Fsearch\",\n",[113,163,165],{"class":115,"line":164},9,[113,166,167],{},"                [\n",[113,169,171],{"class":115,"line":170},10,[113,172,173],{},"                    Headers = [\n",[113,175,177],{"class":115,"line":176},11,[113,178,179],{},"                        #\"Content-Type\" = \"application\u002Fjson\",\n",[113,181,183],{"class":115,"line":182},12,[113,184,185],{},"                        #\"x-api-key\" = apiKey\n",[113,187,189],{"class":115,"line":188},13,[113,190,191],{},"                    ],\n",[113,193,195],{"class":115,"line":194},14,[113,196,197],{},"                    Content = body\n",[113,199,201],{"class":115,"line":200},15,[113,202,203],{},"                ]\n",[113,205,207],{"class":115,"line":206},16,[113,208,209],{},"            ),\n",[113,211,213],{"class":115,"line":212},17,[113,214,215],{},"            parsed = Json.Document(response),\n",[113,217,219],{"class":115,"line":218},18,[113,220,221],{},"            point = parsed[features]{0}[geometry][coordinates]\n",[113,223,225],{"class":115,"line":224},19,[113,226,227],{},"        in\n",[113,229,231],{"class":115,"line":230},20,[113,232,233],{},"            [longitude = point{0}, latitude = point{1}]\n",[113,235,237],{"class":115,"line":236},21,[113,238,239],{},"    ),\n",[113,241,243],{"class":115,"line":242},22,[113,244,245],{},"    Expanded = Table.ExpandRecordColumn(Geocoded, \"geo\", {\"latitude\", \"longitude\"})\n",[113,247,249],{"class":115,"line":248},23,[113,250,251],{},"in\n",[113,253,255],{"class":115,"line":254},24,[113,256,257],{},"    Expanded\n",[10,259,260,263,264,267,268,272],{},[14,261,262],{},"latitude"," and ",[14,265,266],{},"longitude"," land in two new numeric columns. Drop them straight onto the ",[269,270,271],"strong",{},"Map"," visual or use them as the spatial join key against any other geometry layer — lease boundaries, gathering pipelines, fault traces. Refresh runs Power Query again and re-geocodes any new rows.",[10,274,275,276,280],{},"For tables of more than a few hundred wells, swap the per-row pattern for the ",[38,277,279],{"href":278},"\u002Fguides\u002Fapi-integration","Batch API",", which accepts up to 100 PLSS descriptions in a single POST. The same Power Query M language handles it — chunk the column into groups of 100, post each chunk, and append the results.",[30,282,284],{"id":283},"excel-plss-to-latlong-with-lambda-or-power-query","Excel: PLSS to lat\u002Flong with LAMBDA or Power Query",[10,286,287,288,291],{},"Excel gives you two paths. For a one-off workbook with a handful of legal descriptions, a LAMBDA in the ",[269,289,290],{},"Name Manager"," is the lightest option. Define it once, call it like a built-in function:",[104,293,298],{"className":294,"code":296,"language":297},[295],"language-text","=PLSS_TO_LAT(legalDescription) =\n  LAMBDA(desc,\n    LET(\n      url, \"https:\u002F\u002Fdeveloper.townshipamerica.com\u002Fv1\u002Fsearch\",\n      body, \"{\"\"description\"\":\"\"\" & desc & \"\"\"}\",\n      response, WEBSERVICE(url & \"?body=\" & ENCODEURL(body) & \"&key=\" & TA_API_KEY),\n      coord, MID(response, FIND(\"[\", response) + 1, FIND(\"]\", response) - FIND(\"[\", response) - 1),\n      VALUE(TRIM(MID(coord, FIND(\",\", coord) + 1, 20)))\n    )\n  )(legalDescription)\n","text",[14,299,296],{"__ignoreMap":109},[10,301,302,303,306,307,310],{},"Define a matching ",[14,304,305],{},"PLSS_TO_LNG"," for longitude and you can write ",[14,308,309],{},"=PLSS_TO_LAT(B2)"," next to any PLSS cell. The same trick lets you build distance formulas — for example, kilometers from each well to a central gathering plant — without ever leaving the workbook.",[10,312,313,314,317],{},"For anything larger than a few dozen rows, use Power Query inside Excel. The M script above is identical to the Power BI version. Load it from ",[269,315,316],{},"Data → Get & Transform → From Other Sources → Blank Query",", paste, and refresh. Excel writes the latitude\u002Flongitude back into a connected table so the geocoded rows update in place whenever the source PLSS column changes.",[10,319,320],{},"Watch the rate limits. Build-tier API keys allow one request per second; a 5,000-row workbook calling the Search API once per row will hit a 429 response inside a minute. Power Query handles this by buffering, but the cleaner answer is to route anything past 100 rows through the Batch endpoint.",[30,322,324],{"id":323},"google-sheets-the-plss-converter-add-on","Google Sheets: the PLSS converter add-on",[10,326,327,328,332],{},"Google Sheets needs no Power Query equivalent because the ",[38,329,331],{"href":330},"\u002Fguides\u002Fgoogle-sheets-add-on","Township America PLSS Converter add-on"," installs from the Workspace Marketplace and registers two custom functions plus a column-mode sidebar.",[10,334,335],{},"The custom functions read like any other Sheets formula:",[104,337,340],{"className":338,"code":339,"language":297},[295],"=PLSS_TO_LAT(\"T11N R8W Sec 14 NE¼, Indian Meridian\")\n=PLSS_TO_LNG(A2)\n",[14,341,339],{"__ignoreMap":109},[10,343,344,345,348,349,352,353,356,357,360],{},"For the production dashboard pattern — converting an entire column of legal descriptions to lat\u002Flong pairs — open ",[269,346,347],{},"Extensions → Township America → Open Converter",", set the input range (e.g. ",[14,350,351],{},"A2:A500",") and the first output cell (",[14,354,355],{},"B2","), and click ",[269,358,359],{},"Convert Column",". Latitude lands in B, longitude in C, errors in the cell next to whatever row could not be parsed.",[10,362,363,364,367],{},"Free accounts get 10 conversions per calendar month. Paste a Township America API key in ",[269,365,366],{},"Settings"," to remove the limit; the same key works in Power BI and Excel.",[30,369,371],{"id":370},"scaling-to-a-full-oklahoma-well-table","Scaling to a full Oklahoma well table",[10,373,374],{},"A SCOOP\u002FSTACK production dashboard typically pulls from a few thousand active wells across Grady, Stephens, Canadian, Kingfisher, and Blaine counties. Per-row Search API calls work for development, but a refreshable production model wants the Batch API.",[10,376,377],{},"The Batch API accepts up to 100 PLSS descriptions per request. A Build subscription is rated for 1,000 requests per month, which works out to up to 100,000 PLSS descriptions per month at full batch size — enough for a daily refresh of an STACK operator's full Anadarko Basin position with room to spare. Scale ($200\u002Fmo) and Enterprise ($1,000\u002Fmo) raise the monthly quota by 10× and 100× respectively, in case you are converting historical production data alongside the active well list.",[10,379,380],{},"The request body is a flat array of descriptions:",[104,382,386],{"className":383,"code":384,"language":385,"meta":109,"style":109},"language-json shiki shiki-themes material-theme-lighter github-light github-dark","{\n  \"descriptions\": [\n    \"T11N R8W Sec 14 NE¼, Indian Meridian\",\n    \"T12N R8W Sec 22 SW¼, Indian Meridian\",\n    \"T10N R7W Sec 6 NW¼NW¼, Indian Meridian\"\n  ]\n}\n","json",[14,387,388,394,412,426,436,445,450],{"__ignoreMap":109},[113,389,390],{"class":115,"line":116},[113,391,393],{"class":392},"sP7_E","{\n",[113,395,396,400,404,407,409],{"class":115,"line":122},[113,397,399],{"class":398},"s39Yj","  \"",[113,401,403],{"class":402},"sseR_","descriptions",[113,405,406],{"class":398},"\"",[113,408,102],{"class":392},[113,410,411],{"class":392}," [\n",[113,413,414,418,421,423],{"class":115,"line":128},[113,415,417],{"class":416},"sjJ54","    \"",[113,419,20],{"class":420},"s_sjI",[113,422,406],{"class":416},[113,424,425],{"class":392},",\n",[113,427,428,430,432,434],{"class":115,"line":134},[113,429,417],{"class":416},[113,431,83],{"class":420},[113,433,406],{"class":416},[113,435,425],{"class":392},[113,437,438,440,442],{"class":115,"line":140},[113,439,417],{"class":416},[113,441,91],{"class":420},[113,443,444],{"class":416},"\"\n",[113,446,447],{"class":115,"line":146},[113,448,449],{"class":392},"  ]\n",[113,451,452],{"class":115,"line":152},[113,453,454],{"class":392},"}\n",[10,456,457,458,461,462,465,466,469,470,473],{},"The response is a GeoJSON ",[14,459,460],{},"FeatureCollection"," where each ",[14,463,464],{},"Feature.geometry.coordinates"," is a ",[14,467,468],{},"[longitude, latitude]"," pair in the same order as the input. Join it back to your well table on row index and you have a geocoded production model — ready for the Power BI map visual, the Excel 3D Map, or a Sheets ",[14,471,472],{},"GEO_DISTANCE"," calculation against the Cushing tank battery.",[30,475,477],{"id":476},"worked-example-an-oklahoma-stack-production-dashboard","Worked example: an Oklahoma STACK production dashboard",[10,479,480,481,483],{},"Start with a CSV exported from a production accounting system. It has 1,400 rows — every active STACK well operated by a single company across Kingfisher and Blaine counties, with daily oil and gas volumes alongside a ",[14,482,16],{}," column. The goal is a Power BI dashboard with a county-level map, a production trend by section, and a distance-weighted average to the operator's central gathering facility at the Hennessey gas plant.",[10,485,486],{},"Build it in four steps:",[488,489,490,494,497,508],"ol",{},[491,492,493],"li",{},"Load the CSV into Power Query.",[491,495,496],{},"Add the geocoding step from the Power Query snippet above, pointed at the Batch endpoint with 100-row chunks. Total runtime on a Build-tier key: about 14 minutes for the full 1,400 rows, one chunk per 50 seconds with built-in backoff.",[491,498,499,500,503,504,507],{},"Add a calculated column ",[14,501,502],{},"distance_to_hennessey_km"," using the Haversine formula against the gathering plant's coordinates (",[14,505,506],{},"36.1184, -97.8959",").",[491,509,510,511,263,513,515,516,518],{},"Drop ",[14,512,262],{},[14,514,266],{}," onto a Map visual sliced by section and pipe ",[14,517,502],{}," into a stacked bar by quarter.",[10,520,521],{},"The same dashboard built without an API geocoder usually involves a hand-maintained township-range lookup table, a copy-paste workflow with a public PLSS converter web tool, or a vendor delivering coordinates back as a spreadsheet two days later. None of those refresh.",[10,523,524,525,529,530,534],{},"For one-off lookups against a known ",[38,526,528],{"href":527},"\u002Flearn\u002Fmeridians\u002Findian-meridian","Indian Meridian"," township, use the ",[38,531,533],{"href":532},"\u002Foklahoma-plss-converter","Oklahoma PLSS converter"," directly. For everything that needs to live inside Power BI, Excel, or Sheets, the API is the join column you have been missing.",[30,536,538],{"id":537},"pick-the-right-tier","Pick the right tier",[10,540,541,542,545],{},"The Search API ($20\u002Fmo Build, $100\u002Fmo Scale, $500\u002Fmo Enterprise) covers single-record lookups. The Batch API ($40\u002Fmo Build, $200\u002Fmo Scale, $1,000\u002Fmo Enterprise) covers production data pipelines. Subscribe to either from the in-app developer portal — full quotas, code samples, and request logs are on the ",[38,543,544],{"href":40},"API documentation"," page.",[10,547,548,549,551],{},"Try the ",[38,550,41],{"href":40}," — paste a SCOOP or STACK legal description, get back a GeoJSON Feature with the lat\u002Flong centroid, and drop the response into your Power Query, LAMBDA, or Sheets add-on workflow.",[553,554,555],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":109,"searchDepth":122,"depth":122,"links":557},[558,559,560,561,562,563],{"id":32,"depth":122,"text":33},{"id":283,"depth":122,"text":284},{"id":323,"depth":122,"text":324},{"id":370,"depth":122,"text":371},{"id":476,"depth":122,"text":477},{"id":537,"depth":122,"text":538},"2026-06-02T10:00:00Z","Convert Oklahoma section, township, and range to lat\u002Flong for Power BI, Excel, and Google Sheets — Power Query, LAMBDA, and the add-on, with a SCOOP example.",false,"md",{"author":569,"category":570,"faqs":571},"Township America","developer",[572,575,578,581,584],{"question":573,"answer":574},"How do I convert Oklahoma section, township, and range to lat\u002Flong in Power BI?","Add a Power Query step that calls the Township America Search API for each row in your PLSS column. Read the API key from a parameter, pass the legal description in the body, and expand the returned GeoJSON Feature's centroid to two new columns — latitude and longitude. For tables larger than a few hundred rows, switch to the Batch API and post up to 100 descriptions per request.",{"question":576,"answer":577},"Can I geocode PLSS legal descriptions inside an Excel formula?","Yes. Use WEBSERVICE plus a small JSON helper, or — for anything beyond a handful of cells — a LAMBDA wrapper around Power Query. The LAMBDA pattern works against the same Search API endpoint and writes the latitude\u002Flongitude back into adjacent columns, so the legal description and its coordinates live on the same row of your workbook.",{"question":579,"answer":580},"Is there a Google Sheets add-on for PLSS conversion?","Yes. The Township America PLSS Converter add-on installs from the Google Workspace Marketplace and adds two custom functions, PLSS_TO_LAT and PLSS_TO_LNG, plus a sidebar for converting an entire column at once. The free tier covers 10 conversions per calendar month; paste an API key to remove the limit.",{"question":582,"answer":583},"How many PLSS descriptions can the Batch API process at once?","Up to 100 records per request. A Build-tier subscription is rated for 1,000 requests per month, which means up to 100,000 PLSS descriptions per month at full batch size. Scale and Enterprise tiers raise the monthly request quota proportionally.",{"question":585,"answer":586},"Which meridian do I pass for SCOOP\u002FSTACK wells in central Oklahoma?","Indian Meridian for almost all of Oklahoma, including the SCOOP (Anadarko Basin South) and STACK (Anadarko Basin North) plays. The Cimarron Meridian only covers the Panhandle counties — Cimarron, Texas, and Beaver. Pass the meridian as part of the description string; the API resolves the township-range grid against the named meridian, which is what makes T11N R8W Sec 14 in Grady County a different parcel than T11N R8W under any other meridian.",true,"\u002Fblog\u002Fplss-power-bi-excel-google-sheets-oil-gas-analytics",{"title":5,"description":565},"blog\u002Fplss-power-bi-excel-google-sheets-oil-gas-analytics",[592,593,594,595,596,597,598,599,600,601],"Power BI","Excel","Google Sheets","PLSS","API","Oklahoma","SCOOP STACK","Oil and Gas","Developer","Data","8poSLKuIamWWYOXPL7ebJGrh0rkvQVlXmb1T1079Aro"]