diff --git a/README.md b/README.md
index adbbdf2..09ca7c5 100644
--- a/README.md
+++ b/README.md
@@ -39,35 +39,37 @@ $ cat examples/data.json
 
 $ cat examples/config.yml
 ---
-metrics:
-- name: example_global_value
-  path: "{ .counter }"
-  help: Example of a top-level global value scrape in the json
-  labels:
-    environment: beta # static label
-    location: "planet-{.location}"          # dynamic label
+modules:
+  default:
+    metrics:
+    - name: example_global_value
+      path: "{ .counter }"
+      help: Example of a top-level global value scrape in the json
+      labels:
+        environment: beta # static label
+        location: "planet-{.location}"          # dynamic label
 
-- name: example_value
-  type: object
-  help: Example of sub-level value scrapes from a json
-  path: '{.values[?(@.state == "ACTIVE")]}'
-  labels:
-    environment: beta # static label
-    id: '{.id}'          # dynamic label
-  values:
-    active: 1      # static value
-    count: '{.count}' # dynamic value
-    boolean: '{.some_boolean}'
+    - name: example_value
+      type: object
+      help: Example of sub-level value scrapes from a json
+      path: '{.values[?(@.state == "ACTIVE")]}'
+      labels:
+        environment: beta # static label
+        id: '{.id}'          # dynamic label
+      values:
+        active: 1      # static value
+        count: '{.count}' # dynamic value
+        boolean: '{.some_boolean}'
 
-headers:
-  X-Dummy: my-test-header
+    headers:
+      X-Dummy: my-test-header
 
 $ python -m SimpleHTTPServer 8000 &
 Serving HTTP on 0.0.0.0 port 8000 ...
 
 $ ./json_exporter --config.file examples/config.yml &
 
-$ curl "http://localhost:7979/probe?target=http://localhost:8000/examples/data.json" | grep ^example
+$ curl "http://localhost:7979/probe?module=default&target=http://localhost:8000/examples/data.json" | grep ^example
 example_global_value{environment="beta",location="planet-mars"} 1234
 example_value_active{environment="beta",id="id-A"} 1
 example_value_active{environment="beta",id="id-C"} 1
diff --git a/cmd/main.go b/cmd/main.go
index 3f4bcb1..926cd81 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -16,6 +16,7 @@ package cmd
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"net/http"
 	"os"
 
@@ -86,9 +87,19 @@ func probeHandler(w http.ResponseWriter, r *http.Request, logger log.Logger, con
 	defer cancel()
 	r = r.WithContext(ctx)
 
+	module := r.URL.Query().Get("module")
+	if module == "" {
+		module = "default"
+	}
+	if _, ok := config.Modules[module]; !ok {
+		http.Error(w, fmt.Sprintf("Unknown module %q", module), http.StatusBadRequest)
+		level.Debug(logger).Log("msg", "Unknown module", "module", module)
+		return
+	}
+
 	registry := prometheus.NewPedanticRegistry()
 
-	metrics, err := exporter.CreateMetricsList(config)
+	metrics, err := exporter.CreateMetricsList(config.Modules[module])
 	if err != nil {
 		level.Error(logger).Log("msg", "Failed to create metrics list from config", "err", err)
 	}
@@ -102,7 +113,7 @@ func probeHandler(w http.ResponseWriter, r *http.Request, logger log.Logger, con
 		return
 	}
 
-	fetcher := exporter.NewJSONFetcher(ctx, logger, config, r.URL.Query())
+	fetcher := exporter.NewJSONFetcher(ctx, logger, config.Modules[module], r.URL.Query())
 	data, err := fetcher.FetchJSON(target)
 	if err != nil {
 		http.Error(w, "Failed to fetch JSON response. TARGET: "+target+", ERROR: "+err.Error(), http.StatusServiceUnavailable)
diff --git a/cmd/main_test.go b/cmd/main_test.go
index 6bb03d8..1f8b0d3 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -32,9 +32,9 @@ func TestFailIfSelfSignedCA(t *testing.T) {
 	}))
 	defer target.Close()
 
-	req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL, nil)
+	req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
 	recorder := httptest.NewRecorder()
-	probeHandler(recorder, req, log.NewNopLogger(), config.Config{})
+	probeHandler(recorder, req, log.NewNopLogger(), config.Config{Modules: map[string]config.Module{"default": {}}})
 
 	resp := recorder.Result()
 	body, _ := ioutil.ReadAll(resp.Body)
@@ -45,13 +45,21 @@ func TestFailIfSelfSignedCA(t *testing.T) {
 }
 
 func TestSucceedIfSelfSignedCA(t *testing.T) {
-	c := config.Config{}
-	c.HTTPClientConfig.TLSConfig.InsecureSkipVerify = true
+	c := config.Config{
+		Modules: map[string]config.Module{
+			"default": {
+				HTTPClientConfig: pconfig.HTTPClientConfig{
+					TLSConfig: pconfig.TLSConfig{
+						InsecureSkipVerify: true,
+					},
+				},
+			}},
+	}
 	target := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	}))
 	defer target.Close()
 
-	req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL, nil)
+	req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
 	recorder := httptest.NewRecorder()
 	probeHandler(recorder, req, log.NewNopLogger(), c)
 
@@ -63,6 +71,29 @@ func TestSucceedIfSelfSignedCA(t *testing.T) {
 	}
 }
 
+func TestDefaultModule(t *testing.T) {
+	target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+	}))
+	defer target.Close()
+
+	req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL, nil)
+	recorder := httptest.NewRecorder()
+	probeHandler(recorder, req, log.NewNopLogger(), config.Config{Modules: map[string]config.Module{"default": {}}})
+
+	resp := recorder.Result()
+	if resp.StatusCode != http.StatusOK {
+		t.Fatalf("Default module test fails unexpectedly, expected 200, got %d", resp.StatusCode)
+	}
+
+	// Module doesn't exist.
+	recorder = httptest.NewRecorder()
+	probeHandler(recorder, req, log.NewNopLogger(), config.Config{Modules: map[string]config.Module{"foo": {}}})
+	resp = recorder.Result()
+	if resp.StatusCode != http.StatusBadRequest {
+		t.Fatalf("Default module test fails unexpectedly, expected 400, got %d", resp.StatusCode)
+	}
+}
+
 func TestFailIfTargetMissing(t *testing.T) {
 	req := httptest.NewRequest("GET", "http://example.com/foo", nil)
 	recorder := httptest.NewRecorder()
@@ -86,9 +117,9 @@ func TestDefaultAcceptHeader(t *testing.T) {
 	}))
 	defer target.Close()
 
-	req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL, nil)
+	req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
 	recorder := httptest.NewRecorder()
-	probeHandler(recorder, req, log.NewNopLogger(), config.Config{})
+	probeHandler(recorder, req, log.NewNopLogger(), config.Config{Modules: map[string]config.Module{"default": {}}})
 
 	resp := recorder.Result()
 	body, _ := ioutil.ReadAll(resp.Body)
@@ -118,7 +149,7 @@ func TestCorrectResponse(t *testing.T) {
 			t.Fatalf("Failed to load config file %s", test.ConfigFile)
 		}
 
-		req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL+test.ServeFile, nil)
+		req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL+test.ServeFile, nil)
 		recorder := httptest.NewRecorder()
 		probeHandler(recorder, req, log.NewNopLogger(), c)
 
@@ -145,15 +176,21 @@ func TestBasicAuth(t *testing.T) {
 	}))
 	defer target.Close()
 
-	req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL, nil)
+	req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
 	recorder := httptest.NewRecorder()
-	c := config.Config{}
-	auth := &pconfig.BasicAuth{
-		Username: username,
-		Password: pconfig.Secret(password),
+	c := config.Config{
+		Modules: map[string]config.Module{
+			"default": {
+				HTTPClientConfig: pconfig.HTTPClientConfig{
+					BasicAuth: &pconfig.BasicAuth{
+						Username: username,
+						Password: pconfig.Secret(password),
+					},
+				},
+			},
+		},
 	}
 
-	c.HTTPClientConfig.BasicAuth = auth
 	probeHandler(recorder, req, log.NewNopLogger(), c)
 
 	resp := recorder.Result()
@@ -175,11 +212,16 @@ func TestBearerToken(t *testing.T) {
 	}))
 	defer target.Close()
 
-	req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL, nil)
+	req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
 	recorder := httptest.NewRecorder()
-	c := config.Config{}
+	c := config.Config{
+		Modules: map[string]config.Module{"default": {
+			HTTPClientConfig: pconfig.HTTPClientConfig{
+				BearerToken: pconfig.Secret(token),
+			},
+		}},
+	}
 
-	c.HTTPClientConfig.BearerToken = pconfig.Secret(token)
 	probeHandler(recorder, req, log.NewNopLogger(), c)
 
 	resp := recorder.Result()
@@ -206,10 +248,15 @@ func TestHTTPHeaders(t *testing.T) {
 	}))
 	defer target.Close()
 
-	req := httptest.NewRequest("GET", "http://example.com/foo"+"?target="+target.URL, nil)
+	req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
 	recorder := httptest.NewRecorder()
-	c := config.Config{}
-	c.Headers = headers
+	c := config.Config{
+		Modules: map[string]config.Module{
+			"default": {
+				Headers: headers,
+			},
+		},
+	}
 
 	probeHandler(recorder, req, log.NewNopLogger(), c)
 
@@ -264,9 +311,15 @@ func TestBodyPostTemplate(t *testing.T) {
 			w.WriteHeader(http.StatusOK)
 		}))
 
-		req := httptest.NewRequest("POST", "http://example.com/foo"+"?target="+target.URL, strings.NewReader(test.Body.Content))
+		req := httptest.NewRequest("POST", "http://example.com/foo"+"?module=default&target="+target.URL, strings.NewReader(test.Body.Content))
 		recorder := httptest.NewRecorder()
-		c := config.Config{Body: test.Body}
+		c := config.Config{
+			Modules: map[string]config.Module{
+				"default": {
+					Body: test.Body,
+				},
+			},
+		}
 
 		probeHandler(recorder, req, log.NewNopLogger(), c)
 
@@ -351,7 +404,7 @@ func TestBodyPostQuery(t *testing.T) {
 			w.WriteHeader(http.StatusOK)
 		}))
 
-		req := httptest.NewRequest("POST", "http://example.com/foo"+"?target="+target.URL, strings.NewReader(test.Body.Content))
+		req := httptest.NewRequest("POST", "http://example.com/foo"+"?module=default&target="+target.URL, strings.NewReader(test.Body.Content))
 		q := req.URL.Query()
 		for k, v := range test.QueryParams {
 			q.Add(k, v)
@@ -359,7 +412,13 @@ func TestBodyPostQuery(t *testing.T) {
 		req.URL.RawQuery = q.Encode()
 
 		recorder := httptest.NewRecorder()
-		c := config.Config{Body: test.Body}
+		c := config.Config{
+			Modules: map[string]config.Module{
+				"default": {
+					Body: test.Body,
+				},
+			},
+		}
 
 		probeHandler(recorder, req, log.NewNopLogger(), c)
 
diff --git a/config/config.go b/config/config.go
index 2bb4208..c98012b 100644
--- a/config/config.go
+++ b/config/config.go
@@ -46,8 +46,13 @@ const (
 	ValueTypeUntyped ValueType = "untyped"
 )
 
-// Config contains metrics and headers defining a configuration
+// Config contains multiple modules.
 type Config struct {
+	Modules map[string]Module `yaml:"modules"`
+}
+
+// Module contains metrics and headers defining a configuration
+type Module struct {
 	Headers          map[string]string        `yaml:"headers,omitempty"`
 	Metrics          []Metric                 `yaml:"metrics"`
 	HTTPClientConfig pconfig.HTTPClientConfig `yaml:"http_client_config,omitempty"`
@@ -71,15 +76,17 @@ func LoadConfig(configPath string) (Config, error) {
 	}
 
 	// Complete Defaults
-	for i := 0; i < len(config.Metrics); i++ {
-		if config.Metrics[i].Type == "" {
-			config.Metrics[i].Type = ValueScrape
-		}
-		if config.Metrics[i].Help == "" {
-			config.Metrics[i].Help = config.Metrics[i].Name
-		}
-		if config.Metrics[i].ValueType == "" {
-			config.Metrics[i].ValueType = ValueTypeUntyped
+	for _, module := range config.Modules {
+		for i := 0; i < len(module.Metrics); i++ {
+			if module.Metrics[i].Type == "" {
+				module.Metrics[i].Type = ValueScrape
+			}
+			if module.Metrics[i].Help == "" {
+				module.Metrics[i].Help = module.Metrics[i].Name
+			}
+			if module.Metrics[i].ValueType == "" {
+				module.Metrics[i].ValueType = ValueTypeUntyped
+			}
 		}
 	}
 
diff --git a/examples/config.yml b/examples/config.yml
index b4b28f1..d81785a 100644
--- a/examples/config.yml
+++ b/examples/config.yml
@@ -1,44 +1,45 @@
 ---
-metrics:
-- name: example_global_value
-  path: "{ .counter }"
-  help: Example of a top-level global value scrape in the json
-  labels:
-    environment: beta # static label
-    location: "planet-{.location}"          # dynamic label
+modules:
+  default:
+    metrics:
+    - name: example_global_value
+      path: "{ .counter }"
+      help: Example of a top-level global value scrape in the json
+      labels:
+        environment: beta # static label
+        location: "planet-{.location}"          # dynamic label
 
-- name: example_value
-  type: object
-  help: Example of sub-level value scrapes from a json
-  path: '{.values[?(@.state == "ACTIVE")]}'
-  labels:
-    environment: beta # static label
-    id: '{.id}'          # dynamic label
-  values:
-    active: 1      # static value
-    count: '{.count}' # dynamic value
-    boolean: '{.some_boolean}'
+    - name: example_value
+      type: object
+      help: Example of sub-level value scrapes from a json
+      path: '{.values[?(@.state == "ACTIVE")]}'
+      labels:
+        environment: beta # static label
+        id: '{.id}'          # dynamic label
+      values:
+        active: 1      # static value
+        count: '{.count}' # dynamic value
+        boolean: '{.some_boolean}'
+    headers:
+      X-Dummy: my-test-header
 
-headers:
-  X-Dummy: my-test-header
+    # If 'body' is set, it will be sent by the exporter as the body content in the scrape request. The HTTP method will also be set as 'POST' in this case.
+    # body:
+    #   content: |
+    #     {"time_diff": "1m25s", "anotherVar": "some value"}
 
-# If 'body' is set, it will be sent by the exporter as the body content in the scrape request. The HTTP method will also be set as 'POST' in this case.
-# body:
-#   content: |
-#     {"time_diff": "1m25s", "anotherVar": "some value"}
+    # The body content can also be a Go Template (https://golang.org/pkg/text/template), with all the functions from the Sprig library (https://masterminds.github.io/sprig/) available. All the query parameters sent by prometheus in the scrape query to the exporter, are available in the template.
+    # body:
+    #   content: |
+    #     {"time_diff": "{{ duration `95` }}","anotherVar": "{{ .myVal | first }}"}
+    #   templatize: true
 
-# The body content can also be a Go Template (https://golang.org/pkg/text/template), with all the functions from the Sprig library (https://masterminds.github.io/sprig/) available. All the query parameters sent by prometheus in the scrape query to the exporter, are available in the template.
-# body:
-#   content: |
-#     {"time_diff": "{{ duration `95` }}","anotherVar": "{{ .myVal | first }}"}
-#   templatize: true
-
-# For full http client config parameters, ref: https://pkg.go.dev/github.com/prometheus/common/config?tab=doc#HTTPClientConfig
-#
-# http_client_config:
-#   tls_config:
-#     insecure_skip_verify: true
-#   basic_auth:
-#     username: myuser
-#     #password: veryverysecret
-#     password_file: /tmp/mysecret.txt
+    # For full http client config parameters, ref: https://pkg.go.dev/github.com/prometheus/common/config?tab=doc#HTTPClientConfig
+    #
+    # http_client_config:
+    #   tls_config:
+    #     insecure_skip_verify: true
+    #   basic_auth:
+    #     username: myuser
+    #     #password: veryverysecret
+    #     password_file: /tmp/mysecret.txt
diff --git a/examples/prometheus.yml b/examples/prometheus.yml
index 4eb302e..140c8de 100644
--- a/examples/prometheus.yml
+++ b/examples/prometheus.yml
@@ -15,6 +15,8 @@ scrape_configs:
   ## gather the metrics from third party json sources, via the json exporter
 - job_name: json
   metrics_path: /probe
+  params:
+    module: [default]
   static_configs:
     - targets:
       - http://host-1.foobar.com/dummy/data.json
diff --git a/exporter/util.go b/exporter/util.go
index 76d6769..0bbad4c 100644
--- a/exporter/util.go
+++ b/exporter/util.go
@@ -62,7 +62,7 @@ func SanitizeValue(s string) (float64, error) {
 	return value, fmt.Errorf(resultErr)
 }
 
-func CreateMetricsList(c config.Config) ([]JSONMetric, error) {
+func CreateMetricsList(c config.Module) ([]JSONMetric, error) {
 	var (
 		metrics   []JSONMetric
 		valueType prometheus.ValueType
@@ -127,17 +127,17 @@ func CreateMetricsList(c config.Config) ([]JSONMetric, error) {
 }
 
 type JSONFetcher struct {
-	config config.Config
+	module config.Module
 	ctx    context.Context
 	logger log.Logger
 	method string
 	body   io.Reader
 }
 
-func NewJSONFetcher(ctx context.Context, logger log.Logger, c config.Config, tplValues url.Values) *JSONFetcher {
-	method, body := renderBody(logger, c.Body, tplValues)
+func NewJSONFetcher(ctx context.Context, logger log.Logger, m config.Module, tplValues url.Values) *JSONFetcher {
+	method, body := renderBody(logger, m.Body, tplValues)
 	return &JSONFetcher{
-		config: c,
+		module: m,
 		ctx:    ctx,
 		logger: logger,
 		method: method,
@@ -146,7 +146,7 @@ func NewJSONFetcher(ctx context.Context, logger log.Logger, c config.Config, tpl
 }
 
 func (f *JSONFetcher) FetchJSON(endpoint string) ([]byte, error) {
-	httpClientConfig := f.config.HTTPClientConfig
+	httpClientConfig := f.module.HTTPClientConfig
 	client, err := pconfig.NewClientFromConfig(httpClientConfig, "fetch_json", pconfig.WithKeepAlivesDisabled(), pconfig.WithHTTP2Disabled())
 	if err != nil {
 		level.Error(f.logger).Log("msg", "Error generating HTTP client", "err", err)
@@ -161,7 +161,7 @@ func (f *JSONFetcher) FetchJSON(endpoint string) ([]byte, error) {
 		return nil, err
 	}
 
-	for key, value := range f.config.Headers {
+	for key, value := range f.module.Headers {
 		req.Header.Add(key, value)
 	}
 	if req.Header.Get("Accept") == "" {
diff --git a/test/config/good.yml b/test/config/good.yml
index dd6017f..89cccf9 100644
--- a/test/config/good.yml
+++ b/test/config/good.yml
@@ -1,21 +1,22 @@
 ---
-metrics:
-- name: example_global_value
-  path: "{ .counter }"
-  help: Example of a top-level global value scrape in the json
-  labels:
-    environment: beta # static label
-    location: "planet-{.location}"          # dynamic label
-
-- name: example_value
-  type: object
-  help: Example of sub-level value scrapes from a json
-  path: '{.values[?(@.state == "ACTIVE")]}'
-  labels:
-    environment: beta # static label
-    id: '{.id}'          # dynamic label
-  values:
-    active: 1      # static value
-    count: '{.count}' # dynamic value
-    boolean: '{.some_boolean}'
+modules:
+  default:
+    metrics:
+    - name: example_global_value
+      path: "{ .counter }"
+      help: Example of a top-level global value scrape in the json
+      labels:
+        environment: beta # static label
+        location: "planet-{.location}"          # dynamic label
 
+    - name: example_value
+      type: object
+      help: Example of sub-level value scrapes from a json
+      path: '{.values[?(@.state == "ACTIVE")]}'
+      labels:
+        environment: beta # static label
+        id: '{.id}'          # dynamic label
+      values:
+        active: 1      # static value
+        count: '{.count}' # dynamic value
+        boolean: '{.some_boolean}'