docs: def content_ids(params):
      """does the same this as `pageviews`, except it includes content ids and then optionally filters
      the response by a list of content ids passed as query params - note, this load can be a little
      heavy and could take a minute
      """
      # set up default values
      default_from, default_to, yesterday, _ = make_default_times()
  
      # get params
      try:
          series = params.get("site", [DEFAULT_SERIES])[0]
          from_date = params.get("from", [default_from])[0]
          to_date = params.get("to", [default_to])[0]
          group_by = params.get("group_by", [DEFAULT_GROUP_BY])[0]
          ids = params.get("content_id", [])
      except Exception as e:
          LOGGER.exception(e)
          return json.dumps({"error": e.message}), "500 Internal Error"
  
      # check the cache
      cache_key = "{}:{}:{}:{}:{}:{}:{}".format(
          memcached_prefix, "contentids.json", series, from_date, to_date, group_by, ids)
      try:
          data = MEMCACHED_CLIENT.get(cache_key)
          if data:
              return data, "200 OK"
      except Exception as e:
          LOGGER.exception(e)
  
      # enforce content ids
      if not len(ids):
          return json.dumps({"error": "you must pass at least one content id'"}), "400 Bad Request"
  
      # parse from date
      from_date = parse_datetime(from_date)
      if from_date is None:
          LOGGER.error("could not parse 'from'")
          return json.dumps({"error": "could not parse 'from'"}), "400 Bad Request"
  
      # parse to date
      to_date = parse_datetime(to_date)
      if to_date is None:
          LOGGER.error("could not parse 'to'")
          return json.dumps({"error": "could not parse 'to'"}), "400 Bad Request"
  
      # influx will only keep non-aggregated data for a day, so if the from param is beyond that point
      # we need to update the series name to use the rolled up values
      if from_date < yesterday:
          series = update_series(series, "pageviews", "content_id")
  
      # format times
      from_date = format_datetime(from_date)
      to_date = format_datetime(to_date)
  
      # start building the query
      query = "SELECT content_id, sum(value) as value " \
              "FROM {series} " \
              "WHERE time > '{from_date}' AND time < '{to_date}' " \
              "GROUP BY content_id, time({group_by}) " \
              "fill(0);"
      args = {"series": series, "from_date": from_date, "to_date": to_date, "group_by": group_by}
  
      # send the request
      try:
          res = INFLUXDB_CLIENT.query(query.format(**args))
  
      # capture errors and send them back along with the query (for inspection/debugging)
      except Exception as e:
          LOGGER.exception(e)
          return json.dumps({"error": e.message, "query": query.format(**args)}), "500 Internal Error"
  
      # build the response object
      response = flatten_response(res)
  
      # filter by content ids
      if len(ids):
          for site, points in response.items():
              filtered = filter(lambda p: p["content_id"] in ids, points)
              response[site] = filtered
      res = json.dumps(response)
  
      # cache the response
      try:
          MEMCACHED_CLIENT.set(cache_key, res, time=MEMCACHED_EXPIRATION)
      except Exception as e:
          LOGGER.exception(e)
  
      return res, "200 OK"