Grafana dashboards are not just static visualizations; they’re living entities you can manipulate with code.

Let’s see it in action. Imagine you have a Kubernetes cluster and you want a dashboard that shows the CPU and memory usage for pods in a specific namespace, say monitoring. Instead of clicking around in the Grafana UI, you can generate this dashboard JSON and push it via the API.

Here’s a snippet of what a dashboard JSON might look like, simplified for clarity:

{
  "dashboard": {
    "id": null,
    "uid": "my-custom-cpu-mem-dashboard",
    "title": "Pod Resource Usage (Monitoring Namespace)",
    "tags": ["kubernetes", "resources", "monitoring"],
    "timezone": "browser",
    "schemaVersion": 38,
    "version": 1,
    "refresh": "30s",
    "panels": [
      {
        "id": 1,
        "title": "CPU Usage",
        "type": "timeseries",
        "datasource": {
          "type": "prometheus",
          "uid": "prometheus_ds_uid"
        },
        "targets": [
          {
            "expr": "sum(rate(container_cpu_usage_seconds_total{namespace='monitoring', pod!=''}[5m])) by (pod)",

            "legendFormat": "{{pod}}",

            "refId": "A"
          }
        ],
        "gridPos": {
          "h": 8,
          "w": 12,
          "x": 0,
          "y": 0
        }
      },
      {
        "id": 2,
        "title": "Memory Usage",
        "type": "timeseries",
        "datasource": {
          "type": "prometheus",
          "uid": "prometheus_ds_uid"
        },
        "targets": [
          {
            "expr": "sum(container_memory_working_set_bytes{namespace='monitoring', pod!=''}) by (pod)",

            "legendFormat": "{{pod}}",

            "refId": "A"
          }
        ],
        "gridPos": {
          "h": 8,
          "w": 12,
          "x": 12,
          "y": 0
        }
      }
    ]
  },
  "message": "Dashboard saved"
}

This JSON describes a dashboard with two panels: one for CPU usage and one for memory usage. Both panels query a Prometheus data source (you’d need to have a Prometheus data source configured in Grafana, let’s say its UID is prometheus_ds_uid) for pods in the monitoring namespace. The expr fields are Prometheus query language (PromQL) queries. The gridPos determines where the panel sits on the dashboard grid.

The core problem this solves is the manual, repetitive task of creating and updating dashboards, especially in dynamic environments like Kubernetes where services and their metrics change frequently. Programmatic management allows for:

  • Automation: Dashboards can be generated on-demand based on infrastructure changes or new service deployments.
  • Consistency: Ensures all your environments (dev, staging, prod) have identical, well-defined dashboards.
  • Version Control: Dashboard definitions can be stored in Git, enabling tracking of changes, rollbacks, and collaborative development.
  • Integration: Dashboards can be part of CI/CD pipelines, automatically updated when new metrics become available.

The Grafana API is your gateway. The primary endpoints you’ll interact with are for creating and updating dashboards. The most common one is the "Update dashboard" endpoint, which can be found under /api/dashboards/db.

To create a new dashboard, you send a POST request to /api/dashboards/db with the dashboard JSON in the request body. If the dashboard with the specified uid already exists, it will be updated. If uid is null or omitted, Grafana will generate a new one.

To update an existing dashboard, you again use POST to /api/dashboards/db, but this time you must include the uid and the current version of the dashboard in the JSON payload. This prevents accidental overwrites.

Here’s how you might use curl to create or update:

curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_GRAFANA_API_KEY" \
  http://your-grafana-host:3000/api/dashboards/db \
  -d '{
    "dashboard": {
      "id": null,
      "uid": "my-custom-cpu-mem-dashboard",
      "title": "Pod Resource Usage (Monitoring Namespace)",
      "tags": ["kubernetes", "resources", "monitoring"],
      "timezone": "browser",
      "schemaVersion": 38,
      "version": 1,
      "refresh": "30s",
      "panels": [
        // ... panel definitions as above ...
      ]
    },
    "message": "Creating/Updating Pod Resource Dashboard"
  }'

You’ll need to replace YOUR_GRAFANA_API_KEY with a valid API key generated within Grafana (under your user profile settings). The uid is crucial for idempotency – it’s the stable identifier for your dashboard.

The most surprising true thing about Grafana’s API is that the "create" and "update" operations are handled by the same endpoint (POST /api/dashboards/db). The API determines whether to create a new dashboard or update an existing one based on the presence of a uid and its corresponding version in the payload. If you provide a uid that doesn’t exist, it creates it. If you provide a uid that does exist, it attempts to update it, but it requires the version to match the current state of the dashboard in Grafana; otherwise, it will return a version conflict error.

This means your automation scripts can use the exact same API call for both initial creation and subsequent modifications, simplifying your logic. You can fetch the current dashboard JSON using GET /api/dashboards/uid/:uid, modify it in your code, and then POST it back to /api/dashboards/db with the incremented version.

To get the current dashboard JSON and its version, you’d use:

curl -X GET \
  -H "Authorization: Bearer YOUR_GRAFANA_API_KEY" \
  http://your-grafana-host:3000/api/dashboards/uid/my-custom-cpu-mem-dashboard

This response will include the id, uid, version, and the full dashboard object, which you can then modify and resubmit.

The next problem you’ll likely encounter is managing data source configurations programmatically.

Want structured learning?

Take the full Grafana course →