Overview

Windsurf supports an API for custom analytics. It allows querying for data from autocomplete, chat, and command, and a variety of filters, groupings, and aggregations.

We provide all examples in curl; it can then be translated into HTTP requests in other languages.

User Data Analytics API specification

Data from the Users table on the teams page can be obtained with the following command:

curl -X POST --header "Content-Type: application/json" \
--data '{
 "service_key": "<SERVICE_KEY>,
  "group_name": "<GROUP_NAME>",
  "start_timestamp": "<START_TIMESTAMP>",
  "end_timestamp": "<END_TIMESTAMP>"
}' \
https://server.codeium.com/api/v1/UserPageAnalytics

SERVICE_KEY: The service key - an admin user can create one from the service key section of the settings page. The role of the service key must have “Teams Read-only” permissions.

GROUP_NAME: The name of a group to filter on. This field is optional.

START_TIMESTAMP/END_TIMESTAMP: Timestamps in RFC 3339 format, so e.g. 2023-01-01T00:00:00Z

Example output

{
  "userTableStats": [
    {
      "name": "Alice",
      "email": "alice@windsurf.com",
      "lastUpdateTime": "2024-10-10T22:56:10.771591Z",
      "apiKey": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
      "activeDays": 178
    },
    {
      "name": "Bob",
      "email": "bob@windsurf.com",
      "lastUpdateTime": "2024-10-10T18:11:23.980237Z",
      "apiKey": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
      "activeDays": 462
    },
    {
      "name": "Charlie",
      "email": "charlie@windsurf.com",
      "lastUpdateTime": "2024-10-10T16:43:46.117870Z",
      "apiKey": "cccccccc-cccc-cccc-cccc-cccccccccccc",
      "activeDays": 237
    }
  ]
}

Cascade Analytics API Specification

The Cascade specific data seen on the analytics page can be queried via API.

curl -X POST --header "Content-Type: application/json" \
--data '{
  "service_key": "<SERVICE_KEY>",
  "group_name": "<GROUP_NAME>",
  "start_timestamp": "<START_TIMESTAMP>",
  "end_timestamp": "<END_TIMESTAMP>",
  "emails": ["<EMAIL>","<EMAIL>,..."],
  "ide_types": ["<IDE_TYPE>","<IDE_TYPE>,..."],
  "query_requests": [
   {
    "<CASCADE_DATA_SOURCE>": {}
   }
  ]
}' \
https://server.codeium.com/api/v1/CascadeAnalytics

SERVICE_KEY: The service key - an admin user can create a new one from https://windsurf.com/team/team_settings

GROUP_NAME: The name of a group to filter on. This field is optional. Can not be set if emails is set.

START_TIMESTAMP/END_TIMESTAMP: Timestamps in RFC 3339 format, so e.g. 2023-01-01T00:00:00Z

EMAILS: A list of emails to filter on. This field is optional. Can not be set if group_name is set.

IDE_TYPES: A list of IDE types to filter on. This field is optional. The possible values are described below.

QUERY_REQUESTS: A list of query requests to make. This field is required. The possible values of CASCADE_DATA_SOURCE are described below.

Example Query

curl -X POST --header "Content-Type: application/json" \
--data '{
  "service_key": "my_random_test_key",
  "group_name": "my_group_name",
  "start_timestamp": "2025-01-01T00:00:00Z",
  "end_timestamp": "2025-01-02T00:00:00Z",
  "emails": ["my_email@windsurf.com", "my_email2@windsurf.com"],
  "ide_types": ["editor"],
  "query_requests": [
   {
    "cascade_lines": {}
   },
   {
    "cascade_runs": {}
   }
  ]
}' \
https://server.codeium.com/api/v1/CascadeAnalytics

IDE Types

We split cascade data in to two categories, the Editor and the JetBrains Plugin. Excluding the ide_types field in the query returns data for both. If you would like to query data for only one of the IDEs, you can use either of the two options:

  • “editor” for the Windsurf Editor
  • “jetbrains” for the JetBrains Plugin

Cascade Data Sources

There are three possible values for CASCADE_DATA_SOURCE

Source: cascade_lines

Use cascade_lines to query for data on cascade lines suggested and accepted per day.

Example output:

{
  "queryResults": [
    {
      "cascadeLines": {
        "cascadeLines": [
          {
            "day": "2025-05-01T00:00:00Z",
            "linesSuggested": "206",
            "linesAccepted": "157"
          },
          {
            "day": "2025-05-02T00:00:00Z",
            "linesSuggested": "16"
          },
          {
            "day": "2025-05-03T00:00:00Z",
            "linesSuggested": "169",
            "linesAccepted": "168"
          }
        ]
      }
    }
  ]
}

linesSuggested: The number of lines suggested on the given day. linesAccepted: The number of lines accepted on the given day.

Source: cascade_runs

Use cascade_runs to query for data on model usage, credit consumption, and mode.

Example output:

{
  "queryResults": [
    {
      "cascadeRuns": {
        "cascadeRuns": [
          {
            "day": "2025-05-01T00:00:00Z",
            "model": "Claude 3.7 Sonnet (Thinking)",
            "mode": "CONVERSATIONAL_PLANNER_MODE_DEFAULT",
            "messagesSent": "1",
            "cascadeId": "0d35c1f7-0a85-41d0-ac96-a04cd2d64444"
          },
          {
            "day": "2025-05-01T00:00:00Z",
            "model": "Cascade Base",
            "mode": "UNKNOWN",
            "promptsUsed": "125",
            "cascadeId": "0d35c1f7-0a85-41d0-ac96-a04cd2d64444"
          },
          {
            "day": "2025-05-01T00:00:00Z",
            "model": "GPT-4.1 (promo)",
            "mode": "CONVERSATIONAL_PLANNER_MODE_DEFAULT",
            "messagesSent": "5",
            "cascadeId": "1f450ba3-06aa-4ba5-9e12-d3b98c2d33d3"
          },
        ]
      }
    }
  ]
}

day: The date of the run.

model: The model used for the message.

mode: The mode of the run. One of CONVERSATIONAL_PLANNER_MODE_DEFAULT (for write mode), CONVERSATIONAL_PLANNER_MODE_READ_ONLY (for read mode), CONVERSATIONAL_PLANNER_MODE_NO_TOOL (for legacy mode), or UNKNOWN.

messagesSent: The number of messages sent.

cascadeId: The ID of the run. This id can be used to understand how many distinct converstations have been started (as opposed to how many times the user sends a message).

promptsUsed: The number of credits used.

The data returned by the api is in a raw format, which may explain any “UNKNOWN” values. If using this data source for your own metrics, it is recommended to aggregate by the specific metrics you are interested in (eg, count up the promptsUsed field to understand user usage patterns, messagesSent to understand user engagement, etc), as it is possible for mode and prompt data to be split across entries.

Source: cascade_tool_usage

Use cascade_tool_usage to query for data on tool usage. Note that this returns an aggregate count of tool in the provided period.

Example output:

{
  "queryResults": [
    {
      "cascadeToolUsage": {
        "cascadeToolUsage": [
          {
            "tool": "CODE_ACTION",
            "count": "15"
          },
          {
            "tool": "LIST_DIRECTORY",
            "count": "20"
          },
          {
            "tool": "MCP_TOOL",
            "count": "12"
          },
          {
            "tool": "MEMORY",
            "count": "4"
          }
        ]
      }
    }
  ]
}

tool: The tool used for the message.

count: The number of times the tool was used.

Here is a map of the returned enums and the human readable name, as it is displayed in the UI:

  • CODE_ACTION: ‘Code Edit’
  • VIEW_FILE: ‘View File’
  • RUN_COMMAND: ‘Run Command’
  • FIND: ‘Find tool’
  • GREP_SEARCH: ‘Grep Search’
  • VIEW_FILE_OUTLINE: ‘View File Outline’
  • MQUERY: ‘Riptide’
  • LIST_DIRECTORY: ‘List Directory’
  • MCP_TOOL: ‘MCP Tool’
  • PROPOSE_CODE: ‘Propose Code’
  • SEARCH_WEB: ‘Search Web’
  • MEMORY: ‘Memory’
  • PROXY_WEB_SERVER: ‘Browser Preview’
  • DEPLOY_WEB_APP: ‘Deploy Web App’

Custom Analytics API specification

Certain data sources allow for customizable queries via the Custom Analytics API.

Full schemas for selections, filters, aggregations, and orderings are in the following section, in JSON format. Example queries and for each of the three data sources, as well as query debugging tips, will be at the end of the document.

curl -X POST --header "Content-Type: application/json" \
--data '{
  "service_key": "<SERVICE_KEY>",
  "group_name": "<GROUP_NAME>",
  "query_requests": [
    {
      "data_source": "QUERY_DATA_SOURCE_<DATA_SOURCE>",
      "selections": [
        <LIST OF SELECTIONS>
      ],
      "filters": [
        <LIST OF FILTERS>
      ],
      "aggregations": [
        <LIST OF AGGREGATIONS>
      ],
    }
  ]
}' \
https://server.codeium.com/api/v1/Analytics

DATA_SOURCE: select either USER_DATA, CHAT_DATA, or COMMAND_DATA depending on whether you are looking for autocomplete, chat, or command data.

SERVICE_KEY: The service key - an admin user can create a new one from https://windsurf.com/team/team_settings

GROUP_NAME: The name of a group to filter on. This field is optional.

Schemas

Selections

Selections are required. Each selection corresponds to a value to be queried.

{
  "field": "<FIELD_NAME>",
  "name": "<NAME>",
  "aggregation_function": "QUERY_AGGREGATION_<AGGREGATION_FUNCTION>"
}

FIELD_NAME: The field you wish to query. See the Available Fields section below.

NAME: An alias for the field. If not specified, will be the lowercase version of <AGGREGATION_FUNCTION>_<FIELD_NAME>, e.g. sum_num_acceptances. Must be distinct from all other field and aggregation names.

AGGREGATION_FUNCTION: should be one of UNSPECIFIED, COUNT, SUM, AVG, MAX, MIN. If “aggregation_function” is not provided, defaults to UNSPECIFIED.

Filters

Filters are used to narrow down the data to only contain elements that meet certain criteria. They are optional.

{
  "name": "<NAME>",
  "value": "<VALUE>",
  "filter": "QUERY_FILTER_<FILTER>"
}

NAME: The name of the field you wish to filter. If the filtered item is the same as a Selection/Aggregation, this must be equal to the name of the field/aggregation.

VALUE: the value being compared.

FILTER: One of EQUAL, NOT_EQUAL, GREATER_THAN, LESS_THAN, GE (greater than or equal), LE (less than or equal).

Aggregations

Aggregations are used to split the data into groups based off a specified criteria. They are optional.

{
  "field": <FIELD_NAME>,
  "name": <NAME>
}

FIELD_NAME: The field you wish to query. See the Available Fields section.

NAME: An alias for the field. Must be distinct from all other field and aggregation names.

Available Fields

User Data

All data from the USER_DATA source is aggregated per user, per hour.

Note: PCW (percent code written) now has its own table, and does not rely on the user_data table.

Field NameDescriptionValid Aggregations
api_keyA hash of the user API key.UNSPECIFIED, COUNT
dateThe UTC date of the autocompletion(s).UNSPECIFIED, COUNT
date UTC-xThe date of the autocompletion, with a time zone offset. For example, PST would be “date UTC-8”.UNSPECIFIED, COUNT
hourThe UTC hour of the autocompletions(s).UNSPECIFIED, COUNT
languageThe programming language being used.UNSPECIFIED, COUNT
ideThe IDE that was being used.UNSPECIFIED, COUNT
versionThe Windsurf version usedUNSPECIFIED, COUNT
num_acceptancesThe number of times the user accepted an autocomplete. This occurs when the user writes some code, sees grey text, and presses tab.SUM, MAX, MIN, AVG
num_lines_acceptedLines of code accepted from autocomplete.SUM, MAX, MIN, AVG
num_bytes_acceptedBytes accepted from autocomplete.SUM, MAX, MIN, AVG
distinct_usersDistinct users.UNSPECIFIED, COUNT
distinct_developer_daysDistinct (users, day) tuples.UNSPECIFIED, COUNT
distinct_developer_hoursDistinct (users, hour of day) tuples.UNSPECIFIED, COUNT

Chat Data

Note that all data provided in the chat data API are for the chat model responses, not the user questions.

Field NameDescriptionValid Aggregations
api_keyA hash of the user API keyUNSPECIFIED, COUNT
model_idThe chat model’s ID, set during deployment time.UNSPECIFIED, COUNT
dateThe UTC date of the chat response.UNSPECIFIED, COUNT
date UTC-xThe date of the chat response, with a time zone offset. For example, PST would be “date UTC-8”.UNSPECIFIED, COUNT
ideThe IDE that was being usedUNSPECIFIED, COUNT
versionThe Windsurf version usedUNSPECIFIED, COUNT
latest_intent_typeWhether the model response is generated from a code lens or a regular chat. Regular chats will correspond to:

CHAT_INTENT_GENERIC

while code lenses will correspond to one of:

CHAT_INTENT_FUNCTION_EXPLAIN
CHAT_INTENT_FUNCTION_DOCSTRING
CHAT_INTENT_FUNCTION_REFACTOR
CHAT_INTENT_CODE_BLOCK_EXPLAIN
CHAT_INTENT_CODE_BLOCK_REFACTOR
CHAT_INTENT_PROBLEM_EXPLAIN
CHAT_INTENT_FUNCTION_UNIT_TESTS
UNSPECIFIED, COUNT
num_chats_receivedNumber of chats messages sent from Windsurf to the user.SUM, MAX, MIN, AVG
chat_acceptedTrue if a code block was sent in Windsurf’s chat response, and the Thumbs up button is clicked.SUM, COUNT
chat_inserted_at_cursorTrue if a code block was sent in Windsurf’s chat response, and the “Insert” button is clicked.SUM, COUNT
chat_appliedTrue if a code block was sent in Windsurf’s chat response, and the “Apply Diff” button is clicked.SUM, COUNT
chat_loc_usedLines of code used, if a code block was sent in Windsurf’s chat, and any of the “Insert”, “Copy”, or “Apply Diff” buttons are pressed.SUM, MAX, MIN, AVG

Command Data

Note that the Command Data source contains all commands, including those which were declined. The “accepted” field can be used to filter for only accepted commands.

Field NameDescriptionValid Aggregations
api_keyA hash of the user API key.UNSPECIFIED, COUNT
dateThe UTC date of the command.UNSPECIFIED, COUNT
timestampThe UTC timestamp of the command.UNSPECIFIED, COUNT
languageThe programming language being used.UNSPECIFIED, COUNT
ideThe IDE that was being used.UNSPECIFIED, COUNT
versionThe Windsurf version usedUNSPECIFIED, COUNT
command_sourceThe reason that the command was triggered. Valid values are,

COMMAND_REQUEST_SOURCE_LINE_HINT_CODE_LENS
COMMAND_REQUEST_SOURCE_DEFAULT
COMMAND_REQUEST_SOURCE_RIGHT_CLICK_REFACTOR
COMMAND_REQUEST_SOURCE_FUNCTION_CODE_LENS
COMMAND_REQUEST_SOURCE_FOLLOWUP
COMMAND_REQUEST_SOURCE_CLASS_CODE_LENS
COMMAND_REQUEST_SOURCE_PLAN
COMMAND_REQUEST_SOURCE_SELECTION_HINT_CODE_LENS

COMMAND_REQUEST_SOURCE_DEFAULT corresponds to the typical command usage.
UNSPECIFIED, COUNT
provider_sourceDetermines whether command was triggered in generation or edit mode. Valid values are,PROVIDER_SOURCE_COMMAND_GENERATE PROVIDER_SOURCE_COMMAND_EDITUNSPECIFIED, COUNT
lines_addedNumber of lines of code added by the command.SUM, MAX, MIN, AVG
lines_removedNumber of lines of code removed by the command.SUM, MAX, MIN, AVG
bytes_addedNumber of bytes added by the command.SUM, MAX, MIN, AVG
bytes_removedNumber of bytes removed by the command.SUM, MAX, MIN, AVG
selection_linesNumber of lines of code selected by the command. Should always be zero for generations.SUM, MAX, MIN, AVG
selection_bytesNumber of bytes selected by the command. Should always be zero for generations.SUM, MAX, MIN, AVG
acceptedWhether the command was accepted.SUM, COUNT

PCW Data

Valid Selections

Field NameDescriptionValid Aggregations
percent_code_writtenPercent code written. Calculated as (number of windsurf bytes generated) / (number of windsurf bytes generated + number of user bytes generated). This metric can have high variance within single days or users, so we recommend aggregating over weeks for this.UNSPECIFIED
windsurf_bytestotal number of windsurf bytes, which is equal to windsurf_bytes_by_autocomplete + windsurf_bytes_by_commandUNSPECIFIED
user_bytestotal number of user bytesUNSPECIFIED
total_byteswindsurf_bytes + user_bytesUNSPECIFIED
windsurf_bytes_by_autocompletetotal number of windsurf bytes generated from autocompleteUNSPECIFIED
windsurf_bytes_by_commandtotal number of windsurf bytes generated from commandUNSPECIFIED

Valid Filters

Field NameDescriptionSome Examples
api_keyA hash of the user API key.
languageThe programming language being used.KOTLIN, GO, JAVA
ideThe IDE that was being used.jetbrains, vscode
versionThe Windsurf version used1.28.0, 130.0

To filter by date, use start_timestamp and end_timestamp, which should be in RFC 3339 format (e.g. 2023-01-01T00:00:00Z, see in the example below).

Examples

User Data

curl -X POST --header "Content-Type: application/json" \
--data '{
  "service_key": SERVICE_KEY,
  "query_requests": [
    {
      "data_source": "QUERY_DATA_SOURCE_USER_DATA",
      "selections": [
       {
          "field": "num_acceptances",
          "name": "num_acceptances",
          "aggregation_function": "QUERY_AGGREGATION_SUM"
        }
      ],
      "filters": [
        {
          "name": "hour",
          "filter": "QUERY_FILTER_GE",
          "value": "2024-01-01"
        },
        {
          "name": "hour",
          "filter": "QUERY_FILTER_LE",
          "value": "2024-02-01"
        }
      ]
    }
  ]
}' \
https://server.codeium.com/api/v1/Analytics

This query calculates the overall Percent Code Written over the month of January 2024. Example response (JSON formatted for readability):

{
  "queryResults": [
    {
      "responseItems": [
        {
          "item": {
            "num_acceptances": "125",
            "num_lines_accepted": "863"
          }
        }
      ]
    }
  ]
}

Chat Data

curl -X POST --header "Content-Type: application/json" \
--data '{
  "service_key": SERVICE_KEY,
  "query_requests": [
    {
      "data_source": "QUERY_DATA_SOURCE_CHAT_DATA",
      "selections": [
        {
          "field": "chat_loc_used",
          "name": "chat_loc_used",
          "aggregation_function": "QUERY_AGGREGATION_SUM"
        }
      ],
      "filters": [
        {
          "name": "latest_intent_type",
          "filter": "QUERY_FILTER_EQUAL",
          "value": "CHAT_INTENT_FUNCTION_DOCSTRING"
        }
      ],
      "aggregations": [
        {
          "field": "ide",
          "name": "ide"
        }
      ]
    }
  ]
}' \
https://server.codeium.com/api/v1/Analytics

This query shows the number of lines of code accepted from the “Generate Docstring” code lens, for all time, grouped by IDE.

Example response:

{
  "queryResults": [
    {
      "responseItems": [
        {
          "item": {
            "chat_loc_used": "74",
            "ide": "jetbrains"
          },
        },
        {
          "item": {
            "chat_loc_used":"41",
            "ide":"vscode"
          },
        }
      ]
    }
  ]
}

Command Data

curl -X POST --header "Content-Type: application/json" \
--data '{
 "service_key": SERVICE_KEY,
  "query_requests": [
    {
      "data_source": "QUERY_DATA_SOURCE_COMMAND_DATA",
     "selections": [
        {
          "field": "lines_added",
          "name": "lines_added",
          "aggregation_function": "QUERY_AGGREGATION_SUM"
        },
        {
          "field": "lines_removed",
          "name": "lines_removed",
          "aggregation_function": "QUERY_AGGREGATION_SUM"
        }
      ],
      "filters": [
        {
          "name": "provider_source",
          "filter": "QUERY_FILTER_EQUAL",
          "value": "PROVIDER_SOURCE_COMMAND_EDIT"
        },
        {
          "name": "accepted",
          "filter": "QUERY_FILTER_EQUAL",
          "value": "true"
        }
      ],
      "aggregations": [
        {
          "field": "language",
          "name": "language"
        }
      ]
    }
  ]
}' \
https://server.codeium.com/api/v1/Analytics

This query gets the number of lines added and removed from “edit” commands, grouped by programming language.

Example response:

{
  "queryResults": [
    {
      "responseItems": [
        {
          "item": {
            "language": "SHELL",
            "lines_added": "5",
            "lines_removed": "3"
          }
        },
        {
          "item": {
            "language": "GO",
            "lines_added": "31",
            "lines_removed": "27"
          }
        },
        {
          "item": {
            "language": "PYTHON",
            "lines_added": "21",
            "lines_removed": "5"
          }
        },
        {
          "item": {
            "language": "UNSPECIFIED",
            "lines_added": "91",
            "lines_removed": "71"
          }
        },
        {
          "item": {
            "language": "STARLARK",
            "lines_added": "13",
            "lines_removed": "1"
          }
        }
      ]
    }
  ]
}

PCW Data

curl -X POST --header "Content-Type: application/json" \
--data '{
    "service_key": SERVICE_KEY,
    "start_timestamp": "2024-01-01T00:00:00Z",
    "end_timestamp": "2024-12-22T00:00:00Z"
    "query_requests": [
    {
       "data_source": "QUERY_DATA_SOURCE_PCW_DATA",
       "selections": [
          {
             "field": "percent_code_written",
             "name": "percent_code_written"
          },
          {
             "field": "codeium_bytes",
             "name": "codeium_bytes"
          },
          {
             "field": "total_bytes",
             "name": "total_bytes"
          },
          {
             "field": "codeium_bytes_by_autocomplete",
            "name": "codeium_bytes_by_autocomplete"
          },
          {
             "field": "codeium_bytes_by_command",
             "name": "codeium_bytes_by_command"
          }
       ],
     "filters": [
         {
            "filter": "QUERY_FILTER_EQUAL",
            "name": "language",
            "value": "GO"
         }
      ]
    }
  ],
}' \
https: //server.codeium.com/api/v1/Analytics

This query gets the PCW (Percent Code Written) data, along with bytes filtered by language go.

Example response:

{
  "queryResults": [
    {
      "responseItems": [
         {
            "item": {
               "codeium_bytes": "6018",
               "codeium_bytes_by_autocomplete": "4593",
               "codeium_bytes_by_command": "1425",
               "percent_code_written": "0.61",
               "total_bytes": "9900"
             }
         }
     ]
   }
  ]
}

Query debugging

From 1.10.0 and on, invalid queries will return an error message. This section has some common error messages, what they mean, and how to debug the corresponding queries.

Error MessageExplanation
at least one field or aggregation is requiredDid not detect any selections or aggregations - make sure the query request contains at least one.
invalid aggregation function for string type field ide: QUERY_AGGREGATION_SUMOne of the selections used an invalid aggregation function. In this case we tried to use SUM on the “ide” field, but it only supports COUNT and UNSPECIFIED
invalid query table: QUERY_DATA_SOURCE_UNSPECIFIEDThere is likely a typo in the data_source field, double check the spelling.
all selection fields should have an aggregation function, or none of them should

If there are multiple selection fields, either all should contain an aggregation_function, or none of them should. For example, this selection is invalid because num_acceptances is summed, but num_lines_accepted is not:

"selections": [
  {
    "field": "num_acceptances",
    "name": "total_acceptances",
    "aggregation_function": "QUERY_AGGREGATION_SUM"
  },
  {
    "field": "num_lines_accepted",
    "name": "total_lines_accepted",
    "aggregation_function": "QUERY_AGGREGATION_UNSPECIFIED"
  }
]

Note: PCW is always considered aggregated. If no aggregation_function is explicitly chosen, it is considered unspecified. If you would like information about both of these fields, use two seperate queries.

invalid aggregation function for string type field ide: QUERY_AGGREGATION_SUMNot every field supports every aggregation function; see the available fields section for the matches. In this case, the query used a QUERY_AGGREGATION_SUM aggregation function with the “ide” field, which is invalid.
tried to aggregate on a distinct field: distinct_developer_days. Consider aggregating on the non-distinct fields instead: [api_key date]

Fields with the pattern “distinct_*” can not be in the aggregations section; the error suggests an alternative field(s) to aggregate on instead. So instead of:

"aggregations": [
  {
    "field": "distinct_developer_days",
    "name": "distinct_developer_days"
  }
]

Try:

"aggregations": [
  {
    "field": "api_key",
    "name": "api_key"
  },
  {
    "field": "date",
    "name": "date"
  }
]
duplicate field alias for selection/aggregation: num_acceptancesAll selections and aggregations must have a different name. Keep in mind that if the name is not specified, by default it is set to <AGGREGATION_FUNCTION>_<FIELD_NAME>.
invalid group name: GroupNameThe group with the specified name was not found, double check the spelling.