How to delete records via API?

I’ve been able to found the implementation below in grist_api python module but can’t make it working.
Piece of code I’ve found:

def call(self, url, json_data=None, method=None, prefix=None):
    Low-level interface to make a REST call.
    if prefix is None:
      prefix = '/api/docs/%s/' % self._doc_id
    data = json.dumps(json_data, sort_keys=True).encode('utf8') if json_data is not None else None
    method = method or ('POST' if data else 'GET')

    while True:
      full_url = self._server + prefix + url
      if self._dryrun and method != 'GET':"DRYRUN NOT sending %s request to %s", method, full_url)
        return None
      log.debug("sending %s request to %s", method, full_url)
      resp = requests.request(method, full_url, data=data, headers={
        'Authorization': 'Bearer %s' % self._api_key,
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      if not resp.ok:
        # If the error has {"error": ...} content, use the message in the Python exception.
        err_msg = None
          error_obj = resp.json()
          if error_obj and isinstance(error_obj.get("error"), str):
            err_msg = error_obj.get("error")
            # TODO: This is a temporary workaround: SQLITE_BUSY shows up in messages for a
            # temporary problem for which it's safe to retry.
            if 'SQLITE_BUSY' in err_msg:
              log.warn("Retrying after error: %s", err_msg)
        except Exception:   # pylint: disable=broad-except

        if err_msg:
          raise requests.HTTPError(err_msg, response=resp)
          raise resp.raise_for_status()
      return resp.json()

def delete_records(self, table_name, record_ids, chunk_size=None):
    Deletes records from the given table. The data is a list of record IDs.
    # There is an endpoint missing to delete records, but we can use the "apply" endpoint
    # meanwhile.
    for rec_ids in chunks(record_ids, max_size=chunk_size):"delete_records %s %s records", table_name, len(rec_ids))
      data = [['BulkRemoveRecord', table_name, rec_ids]]'apply', json_data=data)

This is how I’m trying to adopt it:

rec_ids = [1,2,3]
headers_post = {
    'Authorization': 'Bearer ' + api_key,
    'Content-Type': 'application/json'
del_url = f'{doc_id}'
del_data = [['BulkRemoveRecord', 'table_name', rec_ids]]
response =, headers=headers_post, json=del_data)

Seems like endpoint ending with just doc_id is not found:
'error': 'not found: '

I’ve found implementation here that is working. Thanks to @Riccardo_Polignieri.
Just for anyone’s reference:

rec_ids = list(range(1, 100))
headers_post = {
    'Authorization': 'Bearer ' + api_key,
    'Content-Type': 'application/json'
url = f'{doc_id}/tables/{table_id}/data/delete'
response =, headers=headers_post, json=rec_ids)
1 Like

Indeed - or, if you install Pygrister, could be something like

from pygrister.api import GristApi
rows = [1, 2, 3]
grist = GristApi(config={'GRIST_API_KEY': '...', 'GRIST_TEAM_SITE': '...', 'GRIST_DOC_ID': '...'})
grist.delete_rows('Mytable', rows)