{"id":2288,"date":"2026-10-13T16:04:24","date_gmt":"2026-10-13T16:04:24","guid":{"rendered":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/?post_type=blog&#038;p=2288"},"modified":"2026-10-13T17:05:53","modified_gmt":"2026-10-13T17:05:53","slug":"designing-meaningful-api-load-tests-with-k6","status":"publish","type":"blog","link":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6","title":{"rendered":"Designing Meaningful API Load Tests with k6"},"content":{"rendered":"\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">API traffic is rarely uniform. A service can receive a mix of requests for known records, frequently accessed records, missing records, and collection endpoints. Treating all of this as one generic load test makes it difficult to understand what a result actually means.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The Hub Domains API uses k6 to model these read paths independently. The goal is not to publish a single capacity benchmark. It is to create a repeatable harness that validates API contracts, highlights path-specific latency changes, and produces evidence engineers can compare across revisions and environments.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">This post describes the test workloads, the rate-driven k6 design behind them, and the operational practices that make the results useful.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-hub-domains-api\">The Hub Domains API<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">ArcGIS Hub sites can be associated with Hub-provided or custom hostnames. The Hub Domains API is the REST-backed registry for those domain records. It connects a hostname to the Hub site and organization it represents, along with configuration used for access, HTTPS behavior, and redirects.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The API supports retrieving a record by hostname or internal ID, listing records by parameters such as site or organization, validating whether a hostname\u2019s CNAME resolves to Hub, and managing the record lifecycle through authenticated create, update, and delete operations.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-is-k6\">What is k6?<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">k6 is an open-source load-testing tool built for developers and performance engineers. Test scripts are written in JavaScript and executed by the k6 runtime, making it possible to define traffic patterns, issue HTTP requests, validate responses, collect metrics, and enforce thresholds in version-controlled code.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Unlike browser-based end-to-end test tools, k6 focuses on protocol-level testing. For the Hub Domains suite, that means k6 sends HTTP requests directly to the API rather than loading a browser page and simulating a user interface workflow.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A k6 script typically contains four parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Test configuration, such as the executor, arrival rate, duration, and virtual-user allocation.<\/li>\n\n\n\n<li>Request logic, such as an HTTP GET request to an API endpoint.<\/li>\n\n\n\n<li>Checks that validate the expected response contract.<\/li>\n\n\n\n<li>Thresholds that determine when a metric should cause the test run to fail.<\/li>\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">k6 supports multiple execution models. The Hub Domains suite uses the&nbsp;<code>ramping-arrival-rate<\/code>&nbsp;executor, which starts iterations at a defined rate rather than asking a fixed number of virtual users to run as quickly as possible. This allows the suite to test whether the API can sustain a known incoming request profile.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The tool also collects standard HTTP metrics, including request duration, failed-request rate, and data transfer. Engineers can add tags to segment those metrics by stable request dimensions, such as the hostname and ID lookup paths used by the Hub Domains suite.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Because the test scripts, fixture configuration, thresholds, and output artifacts can be versioned together, k6 provides a practical foundation for repeatable performance testing in local development, CI pipelines, and approved non-production environments.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-why-a-single-load-test-was-not-enough\">Why a single load test was not enough<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A generic endpoint loop can provide an overall latency number, but it cannot explain which API behavior produced that number.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A repeated lookup for one popular hostname may follow a different cache or data-access path than lookups distributed across many records. A missing hostname has a valid 404 contract, not the same failure semantics as an unexpected server error. Collection requests also have a different query and response shape from individual-record lookups.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">If these request types are mixed into one undifferentiated test, aggregate metrics can hide a regression in one path behind acceptable results in another. The Hub Domains k6 suite addresses that limitation by modeling each read contract independently and tagging hostname and ID traffic for comparison.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The goal is not to publish a single capacity benchmark. It is to create a repeatable harness that validates API contracts, highlights path-specific latency changes, and produces evidence engineers can compare across revisions and environments.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-start-with-workload-contracts\">Start with workload contracts<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Each script encodes one request contract rather than looping over a generic endpoint.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"328\" src=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image-1024x328.png\" alt=\"\" class=\"wp-image-2296\" srcset=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image-1024x328.png 1024w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image-300x96.png 300w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image-768x246.png 768w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image-1536x492.png 1536w, https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image.png 2006w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">This separation matters. A repeated lookup can exercise a different cache or data-access path than a varied lookup. An expected missing record also has a different correctness contract from an unexpected server error.<br><br>Each iteration performs exactly one HTTP GET request, so the configured iteration arrival rate directly represents the intended HTTP request-start rate for that scenario.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-model-incoming-demand-with-arrival-rate\">Model incoming demand with arrival rate<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The suite uses k6&#8217;s ramping-arrival-rate executor rather than a closed virtual-user loop.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A closed model measures how quickly a fixed set of virtual users can complete work. If the API slows down, that model naturally emits fewer requests. An arrival-rate model instead asks whether the service can sustain a defined incoming demand, even when response times change.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The shared profile currently uses these settings:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>const TARGET_RPS = 20;\nconst RAMP_DURATION = &#039;30s&#039;;\nconst STEADY_DURATION = &#039;120s&#039;;\nconst RAMP_DOWN_DURATION = &#039;30s&#039;;<\/code><\/pre><\/div>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Each scenario ramps from zero to 20 requests per second over 30 seconds, holds that rate for two minutes, and ramps down over 30 seconds. Hostname and ID scenarios run concurrently, so lookup workloads target 20 request starts per second per lane, or 40 in total during the steady phase.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">k6 preallocates virtual users for each scenario and can add more when required to maintain the arrival rate.&nbsp;<br>Those virtual-user settings control the capacity of the k6 load generator. They should not be interpreted directly as the application&#8217;s concurrent-request count or as a service-capacity claim.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-tag-the-paths-you-need-to-compare\">Tag the paths you need to compare<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The framework assigns an identifier tag to each scenario and routes it to the appropriate execution function:<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>tags: { identifier: tag },\n\nexec: tag === &#039;hostname&#039; ? &#039;runByHostname&#039; : &#039;runById&#039;<\/code><\/pre><\/div>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">This gives engineers separate hostname and ID metrics without adding a high-cardinality tag for every individual domain. The suite can therefore evaluate both global latency and tagged metrics such as:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>http_req_duration{identifier:hostname}\n\nhttp_req_duration{identifier:id}<\/code><\/pre><\/div>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The shared defaults evaluate a 95th percentile request duration below 1.5 seconds, a 99th percentile below 3 seconds, and a low failed-request rate. Individual workloads can override thresholds when they have more specific expectations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-validate-response-contracts-as-well-as-timing\">Validate response contracts as well as timing<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Fast responses are not necessarily correct responses.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Known-record helpers validate HTTP 200 and confirm that the JSON response contains domain data. The missing-record workload explicitly validates HTTP 404. All shared requests use a consistent header set and a 60-second timeout.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Checks, thresholds, and HTTP failure metrics answer different questions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Checks validate the API response contract.<\/li>\n\n\n\n<li>Thresholds define whether selected metrics should fail a run.<\/li>\n\n\n\n<li>HTTP failure metrics use k6&#8217;s expected-response classification.<\/li>\n<\/ul>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A passing check does not, by itself, make an HTTP response \u201cexpected\u201d for k6&#8217;s built-in HTTP metrics, and a failed check does not necessarily fail the overall test unless a threshold enforces it.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">That distinction is especially important for expected 404 responses. k6 normally counts a 404 as failed for http_req_failed, so the missing-record scenario relies on its explicit status is 404 check to validate the contract.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">For teams using this approach as a strict CI gate, two useful hardening steps are to classify expected responses explicitly and threshold semantic checks:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>const expected404 = http.expectedStatuses(404);\n\nhttp.get(url, {\n ...requestParams(),\n responseCallback: expected404\n});\n\nthresholds: {\n checks: [&#039;rate==1&#039;],\n dropped_iterations: [&#039;count==0&#039;]\n}<\/code><\/pre><\/div>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A checks threshold ensures a contract violation fails the run. Monitoring dropped_iterations verifies that the load generator actually achieved the intended arrival profile.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-keep-inputs-environment-specific\">Keep inputs environment-specific<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The harness selects its configuration through an ENVIRONMENT variable and loads the matching environment data during k6 initialization. This keeps base URLs and fixture values out of the request logic.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A focused run looks like this:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>cd load-testing\n\nENVIRONMENT=qa k6 run scripts\/random-domain.js\n<\/code><\/pre><\/div>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The random workload selects a fixture value on each iteration. The repeated-known and missing-record workloads use designated values. The collection workload builds query parameters with the URL API, using a default limit and an optional page.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Environment-specific configuration makes the scripts reusable, but it does not eliminate the need for test-data stewardship. Fixtures should be safe to exercise, representative of the intended path, and reviewed as the service evolves.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-generate-evidence-not-just-console-output\">Generate evidence, not just console output<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Each run produces a terminal summary, an HTML report, and raw JSON data. The HTML report is useful for quick inspection, while the JSON artifact is better suited for CI retention and deeper comparison.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A useful comparison records the environment, application revision, fixture revision, k6 version, scenario, and arrival profile. Without that context, two results can look comparable while representing different conditions.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The actual load profile should come from the shared configuration and observed k6 metrics rather than hand-maintained summary labels. This keeps report descriptions aligned with the test that actually ran.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-make-the-harness-reproducible-in-ci\">Make the harness reproducible in CI<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A meaningful performance signal must be reproducible by another engineer.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">For automated use, pin the k6 version, retain reports as build artifacts, and version-pin or vendor reporting dependencies. The framework imports report utilities at runtime, so mutable external dependencies can change independently of the test code.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The same discipline applies to infrastructure. A non-production result is a comparative signal, not a production-capacity guarantee. Cache state, database size, network topology, competing workloads, and deployment configuration all affect the outcome.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-lessons-learned\">Lessons Learned<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Building the Hub Domains harness reinforced a few principles that apply to almost any API.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-model-contracts-not-just-endpoints\">Model contracts, not just endpoints<\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">An endpoint can have several meaningful behaviors. For the Domains API, a known-record lookup, a repeated hot-record lookup, a missing-record lookup, and a collection query all exercise different contracts.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A useful load test begins by asking:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>- Which request patterns matter to clients?\n- What does a correct response look like for each pattern?\n- Which paths are likely to have different cache, database, or payload characteristics?<\/code><\/pre><\/div>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">This produces scenarios that can explain a result. A generic request loop can report aggregate latency; contract-based scenarios can identify where behavior changed.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-choose-a-load-model-that-matches-the-question\"><strong>Choose a load model that matches the question<\/strong><\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A virtual-user loop is useful when modeling user workflows. An arrival-rate executor is more appropriate when the question is whether an API can sustain a defined incoming request rate.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The important lesson is to treat load configuration as part of the experiment. The requested rate, ramp profile, virtual-user allocation, and duration should be explicit, versioned, and recorded with the result.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Load generators also have limits. If k6 reports dropped iterations, the test client may not have generated the intended request rate. In that case, the run describes generator capacity as much as API behavior.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-separate-expected-errors-from-failures\"><strong>Separate expected errors from failures<\/strong><\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Not every non-`200` response is a service failure. A `404` for a deliberately missing domain is the expected API contract; a timeout or unexpected `500` is not.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Tests should make that distinction explicit through response checks, expected-status classification, and thresholds. Otherwise, a valid negative-path test can appear unhealthy, or an incorrect response can be hidden in an aggregate success metric.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-make-every-run-reproducible\"><strong>Make every run reproducible<\/strong><\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">A performance result without context is difficult to trust. Record the environment, application revision, fixture revision, k6 version, scenario, and load profile with the output.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Human-readable HTML reports support fast investigation, while raw JSON supports comparison and automation. Both are useful, but neither replaces a clear record of test conditions.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-treat-thresholds-as-engineering-guardrails\"><strong>Treat thresholds as engineering guardrails<\/strong><\/h3>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">Thresholds are not universal definitions of acceptable performance. They are explicit expectations for a known workload in a known environment.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">They should be reviewed as the API, infrastructure, traffic profile, and customer expectations change. A threshold that is too loose fails to detect regressions; one that is too strict creates noise and teaches teams to ignore the signal.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h2>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The Hub Domains k6 suite is more than a collection of request loops. It models distinct API-read contracts, applies a consistent arrival-rate profile, separates hostname and ID metrics, validates response semantics, and produces artifacts that support engineering investigation.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The value of a load-testing harness is not the largest number it can produce. It is the confidence that a known workload can be reproduced, its API contract verified, and a change in behavior explained. By modeling Hub Domains read paths independently and preserving the context around every run, the k6 suite turns performance testing into repeatable engineering evidence.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\">The next step is to keep the suite aligned with real behavior: refresh fixtures, revisit traffic profiles, strengthen CI gates around semantic checks and dropped iterations, and preserve enough execution context to make every result explainable.<\/p>\n\n\n\n<p class=\"undefined block-editor-paragraph wp-block-paragraph\"><\/p>\n","protected":false},"author":34,"featured_media":0,"parent":0,"menu_order":0,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[18,17],"tags":[118,90,116,119,120,117],"class_list":["post-2288","blog","type-blog","status-publish","format-standard","hentry","category-performance","category-testing-qa","tag-api-testing","tag-arcgis-hub","tag-ci-cd","tag-k6","tag-load-testing","tag-performance-testing"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.2 (Yoast SEO v25.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Designing Meaningful API Load Tests with K6 - Esri Software Engineering Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Designing Meaningful API Load Tests with k6\" \/>\n<meta property=\"og:description\" content=\"API traffic is rarely uniform. A service can receive a mix of requests for known records, frequently accessed records, missing records, and collection endpoints. Treating all of this as one generic load test makes it difficult to understand what a result actually means. The Hub Domains API uses k6 to model these read paths independently. [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6\" \/>\n<meta property=\"og:site_name\" content=\"Esri Software Engineering Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-13T17:05:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2006\" \/>\n\t<meta property=\"og:image:height\" content=\"642\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6\",\"url\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6\",\"name\":\"Designing Meaningful API Load Tests with k6 - Esri Software Engineering Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image-1024x328.png\",\"datePublished\":\"2026-10-13T16:04:24+00:00\",\"dateModified\":\"2026-10-13T17:05:53+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#primaryimage\",\"url\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image.png\",\"contentUrl\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image.png\",\"width\":2006,\"height\":642},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Designing Meaningful API Load Tests with k6\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website\",\"url\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/\",\"name\":\"Esri Software Engineering Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Designing Meaningful API Load Tests with K6 - Esri Software Engineering Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6","og_locale":"en_US","og_type":"article","og_title":"Designing Meaningful API Load Tests with k6","og_description":"API traffic is rarely uniform. A service can receive a mix of requests for known records, frequently accessed records, missing records, and collection endpoints. Treating all of this as one generic load test makes it difficult to understand what a result actually means. The Hub Domains API uses k6 to model these read paths independently. [&hellip;]","og_url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6","og_site_name":"Esri Software Engineering Blog","article_modified_time":"2026-10-13T17:05:53+00:00","og_image":[{"width":2006,"height":642,"url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6","url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6","name":"Designing Meaningful API Load Tests with k6 - Esri Software Engineering Blog","isPartOf":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#primaryimage"},"image":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#primaryimage"},"thumbnailUrl":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image-1024x328.png","datePublished":"2026-10-13T16:04:24+00:00","dateModified":"2026-10-13T17:05:53+00:00","breadcrumb":{"@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#primaryimage","url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image.png","contentUrl":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/image.png","width":2006,"height":642},{"@type":"BreadcrumbList","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/articles\/designing-meaningful-api-load-tests-with-k6#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog"},{"@type":"ListItem","position":2,"name":"Designing Meaningful API Load Tests with k6"}]},{"@type":"WebSite","@id":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/#website","url":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/","name":"Esri Software Engineering Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"}]}},"text_date":"October 13, 2026","author_name":"Aniket Kulkarni","author_page":false,"custom_image":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/Designing-Meaningful-API-Load-Tests-with-k6.png","primary_product":false,"tag_data":[{"term_id":118,"name":"API testing","slug":"api-testing","term_group":0,"term_taxonomy_id":118,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":90,"name":"ArcGIS Hub","slug":"arcgis-hub","term_group":0,"term_taxonomy_id":90,"taxonomy":"post_tag","description":"","parent":0,"count":2,"filter":"raw"},{"term_id":116,"name":"CI\/CD","slug":"ci-cd","term_group":0,"term_taxonomy_id":116,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":119,"name":"k6","slug":"k6","term_group":0,"term_taxonomy_id":119,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":120,"name":"Load testing","slug":"load-testing","term_group":0,"term_taxonomy_id":120,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":117,"name":"Performance testing","slug":"performance-testing","term_group":0,"term_taxonomy_id":117,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"}],"category_data":[{"term_id":18,"name":"Performance","slug":"performance","term_group":0,"term_taxonomy_id":18,"taxonomy":"category","description":"Articles on how we optimize the speed, efficiency and performance of our products. For example, by profiling, tuning, monitoring, etc.","parent":3,"count":4,"filter":"raw"},{"term_id":17,"name":"Testing &amp; QA","slug":"testing-qa","term_group":0,"term_taxonomy_id":17,"taxonomy":"category","description":"Articles on how we apply software testing, quality assurance, test automation, and defect tracking processes to ensure software quality.","parent":3,"count":4,"filter":"raw"}],"product_data":{"errors":{"invalid_taxonomy":["Invalid taxonomy."]},"error_data":[]},"primary_product_link":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/","short_description":"Designing load tests for APIs using k6","image":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/app\/uploads\/2026\/10\/Designing-Meaningful-API-Load-Tests-with-k6-826x465-1.png","_links":{"self":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/article\/2288","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/article"}],"about":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/users\/34"}],"version-history":[{"count":0,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/article\/2288\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/media?parent=2288"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/categories?post=2288"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.esri.com\/en-us\/software-engineering\/blog\/wp-json\/wp\/v2\/tags?post=2288"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}