Bulk Upload Vehicle Consumptions
const options = {
method: 'POST',
headers: {
'x-api-key': '<x-api-key>',
'x-organization-id': '<x-organization-id>',
'x-user-id': '<x-user-id>',
'Content-Type': 'application/json'
},
body: JSON.stringify({file_name: '<string>'})
};
fetch('https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv"
payload = { "file_name": "<string>" }
headers = {
"x-api-key": "<x-api-key>",
"x-organization-id": "<x-organization-id>",
"x-user-id": "<x-user-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)curl --request POST \
--url https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--header 'x-organization-id: <x-organization-id>' \
--header 'x-user-id: <x-user-id>' \
--data '
{
"file_name": "<string>"
}
'{
"upload_url": "<string>",
"file_name": "<string>",
"file_id": "<string>",
"destination_file_key": "<string>",
"message": "<string>"
}Bulk Upload Vehicle Consumptions
Get a presigned URL to upload multiple vehicle consumption records via CSV
POST
/
api
/
v1
/
vehicle_consumptions
/
bulk
/
csv
Bulk Upload Vehicle Consumptions
const options = {
method: 'POST',
headers: {
'x-api-key': '<x-api-key>',
'x-organization-id': '<x-organization-id>',
'x-user-id': '<x-user-id>',
'Content-Type': 'application/json'
},
body: JSON.stringify({file_name: '<string>'})
};
fetch('https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv"
payload = { "file_name": "<string>" }
headers = {
"x-api-key": "<x-api-key>",
"x-organization-id": "<x-organization-id>",
"x-user-id": "<x-user-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)curl --request POST \
--url https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--header 'x-organization-id: <x-organization-id>' \
--header 'x-user-id: <x-user-id>' \
--data '
{
"file_name": "<string>"
}
'{
"upload_url": "<string>",
"file_name": "<string>",
"file_id": "<string>",
"destination_file_key": "<string>",
"message": "<string>"
}Bulk Upload Vehicle Consumptions
Upload hundreds of vehicle fuel consumption records at once via a CSV file. This endpoint returns a presigned S3 URL so you can upload your file, which will be processed asynchronously.This method is recommended for bulk uploads. Consumption records are persisted in the database and emissions are calculated automatically based on fuel type and quantity.
Upload Flow
1
Request presigned URL
Make a POST request to
/api/v1/vehicle_consumptions/bulk/csv with your file name2
Upload to S3
Use the presigned URL to upload your CSV directly to S3 (PUT request)
3
Asynchronous processing
The system processes your file in the background, creating consumption records and calculating emissions
4
Verify results
Check consumption records with the List Vehicle Consumptions endpoint
Step 1: Request Presigned URL
Request
Headers
string
required
Your API key for authenticationExample:
sk_live_1234567890abcdefstring
required
Your organization UUIDExample:
ff4adcc7-8172-45fe-9cf1-e90a6de53aa9string
required
Your user UUIDExample:
a1b2c3d4-e5f6-7890-abcd-ef1234567890Body Parameters
string
required
Name of the CSV file you’re going to uploadExample:
"2024_vehicle_fuel_records.csv"Response
string
Presigned S3 URL to upload the consumption records file
string
Sanitized file name (alphanumeric only)
string
File UUID for tracking
string
Full S3 key where file will be stored
string
Confirmation message
Example
curl -X POST "https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv" \
-H "Authorization: Bearer ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "x-user-id: ${DCYCLE_USER_ID}" \
-H "Content-Type: application/json" \
-d '{
"file_name": "2024_vehicle_fuel_records.csv"
}'
import requests
import os
api_key = os.getenv("DCYCLE_API_KEY")
org_id = os.getenv("DCYCLE_ORG_ID")
user_id = os.getenv("DCYCLE_USER_ID")
headers = {
"Authorization": f"Bearer {api_key}",
"x-organization-id": org_id,
"x-user-id": user_id,
"Content-Type": "application/json"
}
payload = {
"file_name": "2024_vehicle_fuel_records.csv"
}
response = requests.post(
"https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv",
headers=headers,
json=payload
)
result = response.json()
upload_url = result["upload_url"]
file_id = result["file_id"]
print(f"Upload URL obtained: {upload_url[:50]}...")
print(f"File ID: {file_id}")
const axios = require('axios');
const apiKey = process.env.DCYCLE_API_KEY;
const orgId = process.env.DCYCLE_ORG_ID;
const userId = process.env.DCYCLE_USER_ID;
const headers = {
'Authorization': `Bearer ${apiKey}`,
'x-organization-id': orgId,
'x-user-id': userId,
'Content-Type': 'application/json'
};
const payload = {
file_name: '2024_vehicle_fuel_records.csv'
};
axios.post(
'https://api.dcycle.io/api/v1/vehicle_consumptions/bulk/csv',
payload,
{ headers }
)
.then(response => {
const { upload_url, file_id } = response.data;
console.log('Upload URL:', upload_url.substring(0, 50) + '...');
console.log('File ID:', file_id);
})
.catch(error => console.error(error));
Successful Response
{
"upload_url": "https://dcycle-vehicle-consumptions.s3.amazonaws.com/files/dcycle/...",
"file_name": "2024_vehicle_fuel_records",
"file_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"destination_file_key": "files/dcycle/.../2024_vehicle_fuel_records.csv",
"message": "Upload pre-signed url generated"
}
Step 2: Upload CSV to S3
Use the presigned URL to upload your CSV file directly to S3:# The presigned URL already includes authentication parameters
curl -X PUT "${UPLOAD_URL}" \
-H "Content-Type: text/csv" \
--data-binary @2024_vehicle_fuel_records.csv
import requests
# Read CSV file
with open('2024_vehicle_fuel_records.csv', 'rb') as file:
csv_data = file.read()
# Upload to S3
response = requests.put(
upload_url,
data=csv_data,
headers={'Content-Type': 'text/csv'}
)
if response.status_code == 200:
print("✅ File uploaded successfully")
else:
print(f"❌ Error: {response.status_code}")
const fs = require('fs');
const axios = require('axios');
// Read CSV file
const csvData = fs.readFileSync('2024_vehicle_fuel_records.csv');
// Upload to S3
axios.put(uploadUrl, csvData, {
headers: { 'Content-Type': 'text/csv' }
})
.then(() => console.log('✅ File uploaded successfully'))
.catch(error => console.error('❌ Error:', error));
The presigned URL expires in 15 minutes. Make sure to upload the file within that time.
CSV Format
The CSV must follow this structure for creating vehicle consumption records:vehicle_id,quantity,unit_id,start_date,end_date,custom_id,base_total_spend,currency_unit
a1b2c3d4-e5f6-7890-abcd-ef1234567890,45.5,liter-unit-uuid,2024-01-15,2024-01-15,INV-2024-001,68.25,euros_(eur)
b2c3d4e5-f6g7-8901-bcde-fg2345678901,52.0,liter-unit-uuid,2024-01-10,2024-01-10,INV-2024-002,78.00,euros_(eur)
c3d4e5f6-g7h8-9012-cdef-gh3456789012,38.2,liter-unit-uuid,2024-01-08,2024-01-08,INV-2024-003,,
Required Columns
| Column | Description | Valid Values | Example |
|---|---|---|---|
vehicle_id | UUID of the vehicle | UUID from /api/v1/vehicles | a1b2c3d4-... |
quantity | Fuel quantity consumed | Positive number | 45.5 |
unit_id | UUID of measurement unit | UUID from /api/v1/units | liter-unit-uuid |
start_date | Consumption period start | YYYY-MM-DD format | 2024-01-15 |
end_date | Consumption period end | YYYY-MM-DD format | 2024-01-15 |
Optional Columns
| Column | Description | Example |
|---|---|---|
custom_id | External reference ID (fuel card, invoice number) | FUELCARD-2024-001 |
base_total_spend | Total cost of the consumption | 120.50 |
currency_unit | Currency name for the spend amount | euros_(eur) |
Column Details
vehicle_id:- Must be a valid UUID from your vehicles list
- Get valid vehicle IDs:
GET /api/v1/vehicles - Vehicle must belong to your organization
- Must be positive number
- No negative values allowed
- Precision: up to 6 decimal places
- Must be a valid UUID from the Units endpoint
- Typically liquid units (L, gal) for combustion vehicles
- Energy units (kWh) for electric vehicles
- Get valid unit IDs:
GET /api/v1/units
- Format:
YYYY-MM-DD - Must be today or in the past
- end_date must be >= start_date
- Can be same day for single refueling events
- Used to track consumption periods
- External reference for tracking
- Examples: fuel card transaction ID, invoice number, receipt ID
- Useful for reconciliation with fuel card systems
- Max length: 255 characters
- Total monetary cost of the consumption
- Negative values are accepted
- Can be provided independently of
currency_unit
- Name of the currency for the spend amount
- Must match a valid fiat currency unit name (e.g.,
euros_(eur),us_dollar_(usd),british_pound_(gbp)) - Can be provided independently of
base_total_spend - Get valid currency names:
GET /api/v1/units?type=fiat_currency
Download Template
Download CSV Template
Template with correct format and sample data
Step 3: Asynchronous Processing
Once the CSV is uploaded:- Validation: The system validates format, vehicle IDs, units, and dates
- Processing: Each row is processed and consumption records are created
- Emission Calculation: CO2e values calculated using vehicle fuel type and emission factors
- Status Update: Records marked as
activeorerrorbased on processing result - Notification: (Coming soon) You’ll receive a webhook when complete
Processing can take from a few seconds to several minutes depending on file size and number of records.
Step 4: Verify Results
After processing, you can query consumption records for each vehicle:# List consumptions for a specific vehicle
curl "https://api.dcycle.io/api/v1/vehicle_consumptions/vehicle/{vehicle_id}" \
-H "Authorization: Bearer ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "x-user-id: ${DCYCLE_USER_ID}"
Complete Example Script
import requests
import os
from time import sleep
class DcycleVehicleConsumptions:
def __init__(self, api_key, org_id, user_id):
self.api_key = api_key
self.org_id = org_id
self.user_id = user_id
self.base_url = "https://api.dcycle.io"
self.headers = {
"Authorization": f"Bearer {api_key}",
"x-organization-id": org_id,
"x-user-id": user_id,
"Content-Type": "application/json"
}
def upload_csv(self, csv_file_path):
"""Complete CSV upload: presigned URL + upload + verification"""
# 1. Get presigned URL
file_name = os.path.basename(csv_file_path)
response = requests.post(
f"{self.base_url}/api/v1/vehicle_consumptions/bulk/csv",
headers=self.headers,
json={"file_name": file_name}
)
response.raise_for_status()
upload_url = response.json()["upload_url"]
file_id = response.json()["file_id"]
print(f"✅ Presigned URL obtained (File ID: {file_id})")
# 2. Upload file
with open(csv_file_path, 'rb') as f:
upload_response = requests.put(
upload_url,
data=f,
headers={'Content-Type': 'text/csv'}
)
upload_response.raise_for_status()
print(f"✅ File uploaded successfully")
# 3. Wait for processing (optional)
print("⏳ Processing... (this may take a few minutes)")
sleep(30) # Wait 30 seconds
print("✅ Processing complete. Verify consumption records by vehicle.")
return file_id
def get_vehicle_consumptions(self, vehicle_id, page=1, size=50):
"""Get consumptions for a specific vehicle"""
response = requests.get(
f"{self.base_url}/api/v1/vehicle_consumptions/vehicle/{vehicle_id}",
headers=self.headers,
params={"page": page, "size": size}
)
response.raise_for_status()
return response.json()
def check_upload_errors(self, vehicle_ids):
"""Check for consumption records with errors"""
total_errors = 0
for vehicle_id in vehicle_ids:
response = requests.get(
f"{self.base_url}/api/v1/vehicle_consumptions/vehicle/{vehicle_id}",
headers=self.headers,
params={"page": 1, "size": 1}
)
data = response.json()
if data['total_error'] > 0:
print(f"⚠️ Vehicle {vehicle_id}: {data['total_error']} errors")
total_errors += data['total_error']
if total_errors == 0:
print("✅ All consumption records processed successfully")
return total_errors
# Usage
if __name__ == "__main__":
client = DcycleVehicleConsumptions(
api_key=os.getenv("DCYCLE_API_KEY"),
org_id=os.getenv("DCYCLE_ORG_ID"),
user_id=os.getenv("DCYCLE_USER_ID")
)
# Upload consumptions
file_id = client.upload_csv("2024_vehicle_fuel_records.csv")
# Check for errors (provide vehicle IDs from your CSV)
vehicle_ids = [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6g7-8901-bcde-fg2345678901"
]
errors = client.check_upload_errors(vehicle_ids)
#!/bin/bash
# Variables
API_KEY="${DCYCLE_API_KEY}"
ORG_ID="${DCYCLE_ORG_ID}"
USER_ID="${DCYCLE_USER_ID}"
CSV_FILE="2024_vehicle_fuel_records.csv"
BASE_URL="https://api.dcycle.io"
# 1. Get presigned URL
echo "📤 Requesting presigned URL..."
RESPONSE=$(curl -s -X POST "${BASE_URL}/api/v1/vehicle_consumptions/bulk/csv" \
-H "Authorization: Bearer ${API_KEY}" \
-H "x-organization-id: ${ORG_ID}" \
-H "x-user-id: ${USER_ID}" \
-H "Content-Type: application/json" \
-d "{\"file_name\": \"${CSV_FILE}\"}")
UPLOAD_URL=$(echo $RESPONSE | jq -r '.upload_url')
FILE_ID=$(echo $RESPONSE | jq -r '.file_id')
echo "✅ URL obtained (File ID: ${FILE_ID})"
# 2. Upload file
echo "📤 Uploading CSV file..."
curl -X PUT "${UPLOAD_URL}" \
-H "Content-Type: text/csv" \
--data-binary @"${CSV_FILE}"
echo "✅ File uploaded successfully"
echo "⏳ File is being processed..."
Common Errors
400 Bad Request - Missing file_name
Cause: Field required{
"detail": "Field required",
"field": "file_name"
}
file_name parameter in the request body.
403 Forbidden - URL Expired
Cause: The presigned URL expired (15 minutes) Solution: Request a new presigned URL and upload the file immediately.422 Validation Error - CSV Format
Cause: The CSV has incorrect format or missing required columns Solution: Verify that your CSV has all required columns (vehicle_id, quantity, unit_id, start_date, end_date) and correct format.
422 Validation Error - Invalid Vehicle ID
Cause:vehicle_id doesn’t exist or doesn’t belong to your organization
Solution: Use the Vehicles endpoint to get valid vehicle IDs from your fleet.
422 Validation Error - Invalid Unit ID
Cause:unit_id doesn’t exist or is not compatible with vehicle fuel type
Solution: Use the Units endpoint to get valid unit IDs. For liquid fuels use L, for electric use kWh.
422 Validation Error - Invalid Date
Cause: Date is in the future or incorrectly formatted Solution: Ensure all dates are today or in the past, and useYYYY-MM-DD format.
422 Validation Error - End Date Before Start Date
Cause:end_date is earlier than start_date
Solution: Ensure end_date >= start_date.
Limits and Recommendations
| Limit | Value |
|---|---|
| Maximum file size | 100 MB |
| Maximum rows per CSV | 100,000 |
| URL expiration time | 15 minutes |
| Processing time | ~1 second per 200 records |
For very large datasets (>100k records), consider splitting them into multiple smaller CSVs by vehicle or time period.
Best Practices
Data Preparation
- Get Vehicle IDs first: Query your vehicles before creating the CSV
- Get Unit IDs: Ensure unit IDs match vehicle fuel types (L for liquid fuels, kWh for electric)
- Validate dates: All dates must be today or in the past
- Use custom_id: Include external references (fuel card IDs) for reconciliation
- Group by refueling events: One row per refueling event or period
Error Handling
After upload, check for consumption records withstatus: "error":
def check_for_errors_by_vehicle(vehicle_ids):
"""Check if any consumption records failed to process"""
for vehicle_id in vehicle_ids:
response = requests.get(
f"https://api.dcycle.io/api/v1/vehicle_consumptions/vehicle/{vehicle_id}",
headers=headers,
params={"filter_by": "status:eqerror"}
)
consumptions = response.json()
if consumptions['total'] > 0:
print(f"⚠️ Vehicle {vehicle_id}: {consumptions['total']} errors")
for consumption in consumptions['items']:
print(f" - {consumption.get('start_date', 'N/A')}: {consumption.get('quantity')} {consumption['unit']['name']}")
# Check after processing
check_for_errors_by_vehicle(["vehicle-uuid-1", "vehicle-uuid-2"])
Incremental Updates
To add new consumption records:- Get latest consumption date for each vehicle
- Export new records from fuel card system
- Upload only new records (avoid duplicates)
Data Sources
Common sources for vehicle consumption data:- Fuel Card Systems: Transaction exports from providers (Shell, BP, Repsol)
- Telematics: GPS fleet tracking systems with fuel monitoring
- Manual Logs: Driver-reported fuel purchases
- Accounting Systems: Fuel expense records
- Electric Vehicle Charging: Charging station APIs or apps
Integration Examples
Fuel Card System Integration
def import_from_fuel_card_system(fuel_card_export_file):
"""Convert fuel card export to Dcycle format"""
import pandas as pd
# Read fuel card export
df = pd.read_csv(fuel_card_export_file)
# Get vehicle mapping (license plate -> vehicle_id)
vehicles_response = requests.get(
"https://api.dcycle.io/api/v1/vehicles",
headers=headers,
params={"size": 1000}
)
vehicles = vehicles_response.json()['items']
license_to_id = {v['license_plate']: v['id'] for v in vehicles}
# Get unit ID for liters
units_response = requests.get(
"https://api.dcycle.io/api/v1/units",
headers=headers,
params={"type": "liquid"}
)
units = units_response.json()
liter_unit = next(u for u in units if u['name'] == 'L')
# Transform to Dcycle format
dcycle_records = []
for _, row in df.iterrows():
vehicle_id = license_to_id.get(row['license_plate'])
if vehicle_id:
dcycle_records.append({
'vehicle_id': vehicle_id,
'quantity': row['liters'],
'unit_id': liter_unit['id'],
'start_date': row['transaction_date'],
'end_date': row['transaction_date'],
'custom_id': row['transaction_id']
})
# Create CSV
dcycle_df = pd.DataFrame(dcycle_records)
dcycle_df.to_csv('dcycle_consumptions.csv', index=False)
print(f"✅ Converted {len(dcycle_records)} fuel card transactions")
return 'dcycle_consumptions.csv'
Electric Vehicle Charging Integration
def import_ev_charging_data(charging_data):
"""Import electric vehicle charging data"""
# Get kWh unit
units_response = requests.get(
"https://api.dcycle.io/api/v1/units",
headers=headers,
params={"type": "energy"}
)
units = units_response.json()
kwh_unit = next(u for u in units if u['name'] == 'kWh')
# Transform charging data
dcycle_records = []
for charge in charging_data:
dcycle_records.append({
'vehicle_id': charge['vehicle_id'],
'quantity': charge['kwh_delivered'],
'unit_id': kwh_unit['id'],
'start_date': charge['charge_date'],
'end_date': charge['charge_date'],
'custom_id': f"CHARGE-{charge['session_id']}"
})
return dcycle_records
Related Endpoints
List Vehicle Consumptions
View uploaded consumption records
List Vehicles
Get vehicle IDs for your fleet
List Units
Get measurement units for consumptions
Authentication
Learn about API authentication
Was this page helpful?