Skip to main content
POST
https://api.dcycle.io
/
api
/
v1
/
vehicles
/
bulk
/
csv
Bulk Upload Vehicles
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/vehicles/bulk/csv', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
{
  "upload_url": "<string>",
  "file_name": "<string>",
  "file_id": "<string>",
  "destination_file_key": "<string>",
  "message": "<string>"
}

Bulk Upload Vehicles

Upload hundreds of vehicles 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. Vehicles are persisted in the database and emissions are calculated automatically based on consumption data.

Upload Flow

1

Request presigned URL

Make a POST request to /api/v1/vehicles/bulk/csv with your file name
2

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 vehicles and validating data
4

Verify results

Check your fleet with the List Vehicles endpoint

Step 1: Request Presigned URL

Request

Headers

x-api-key
string
required
Your API key for authenticationExample: sk_live_1234567890abcdef
x-organization-id
string
required
Your organization UUIDExample: ff4adcc7-8172-45fe-9cf1-e90a6de53aa9
x-user-id
string
required
Your user UUIDExample: a1b2c3d4-e5f6-7890-abcd-ef1234567890

Body Parameters

file_name
string
required
Name of the CSV file you’re going to uploadExample: "company_fleet_2024.csv"

Response

upload_url
string
Presigned S3 URL to upload the vehicles file
file_name
string
Sanitized file name (alphanumeric only)
file_id
string
File UUID for tracking
destination_file_key
string
Full S3 key where file will be stored
message
string
Confirmation message

Example

curl -X POST "https://api.dcycle.io/api/v1/vehicles/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": "company_fleet_2024.csv"
  }'

Successful Response

{
  "upload_url": "https://dcycle-vehicles.s3.amazonaws.com/files/dcycle/...",
  "file_name": "company_fleet_2024",
  "file_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "destination_file_key": "files/dcycle/.../company_fleet_2024.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 @company_fleet_2024.csv
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 vehicles:
name,license_plate,type,ownership,country,is_known,known_vehicle_id,vehicle_fuel_id,registration_year,market_segment,size
Company Van #1,1234-ABC,freight,owned,ES,true,b2c3d4e5-f6g7-8901-bcde-fg2345678901,c3d4e5f6-g7h8-9012-cdef-gh3456789012,2020,lower_medium,medium
CEO Car,9876-XYZ,passenger,owned,ES,true,e5f6g7h8-i9j0-1234-efgh-ij5678901234,f6g7h8i9-j0k1-2345-fghi-jk6789012345,2023,executive,large_car
Delivery Truck,5555-DEF,freight,rented,ES,false,,i9j0k1l2-m3n4-5678-ijkl-mn9012345678,2019,,

Required Columns

ColumnDescriptionValid ValuesExample
typeVehicle usage typepassenger, freight"freight"
ownershipOwnership statusowned, rented"owned"
countryISO 3166-1 alpha-2 country codeES, FR, DE, etc."ES"
is_knownWhether from known databasetrue, falsetrue

Optional Columns

ColumnDescriptionExample
nameVehicle name or description"Company Van #1"
license_plateLicense plate number"1234-ABC"
known_vehicle_idUUID from known vehicles database (if is_known: true)UUID string
vehicle_fuel_idUUID of fuel typeUUID string
registration_yearYear first registered2020
market_segmentMarket segment (for passenger vehicles)mini, supermini, executive, etc.
sizeVehicle size classificationsmall_car, medium, large_car, average_car

Column Details

is_known:
  • true: Vehicle from Dcycle’s known vehicles database. Must provide known_vehicle_id.
  • false: Custom vehicle. System will create an unknown vehicle entry.
type:
  • passenger: Company cars, executive vehicles, personal use
  • freight: Delivery vans, trucks, commercial vehicles
ownership:
  • owned: Vehicles owned by your organization
  • rented: Leased or rented vehicles
market_segment (optional, for passenger vehicles):
  • mini - City cars
  • supermini - Small cars
  • lower_medium - Compact cars
  • upper_medium - Mid-size cars
  • executive - Large cars
  • luxury - Premium cars
  • sports - Sports cars
  • dual_purpose_4x4 - SUVs
  • mpv - Minivans

Download Template

Download CSV Template

Template with correct format and sample data

Step 3: Asynchronous Processing

Once the CSV is uploaded:
  1. Validation: The system validates format, vehicle IDs, and data
  2. Processing: Each row is processed and vehicles are created
  3. Emission Calculation: CO2e values initialized to 0 (updated when consumption data is added)
  4. Notification: (Coming soon) You’ll receive a webhook when complete
Processing can take from a few seconds to several minutes depending on file size.

Step 4: Verify Results

After processing, you can query your vehicles:
# List vehicles
curl "https://api.dcycle.io/api/v1/vehicles" \
  -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 DcycleVehicles:
    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/vehicles/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

        # 4. Verify vehicles
        vehicles = self.get_vehicles()
        print(f"βœ… Total vehicles in fleet: {vehicles['total']}")

        return file_id

    def get_vehicles(self):
        """List vehicles"""
        response = requests.get(
            f"{self.base_url}/api/v1/vehicles",
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()

# Usage
if __name__ == "__main__":
    client = DcycleVehicles(
        api_key=os.getenv("DCYCLE_API_KEY"),
        org_id=os.getenv("DCYCLE_ORG_ID"),
        user_id=os.getenv("DCYCLE_USER_ID")
    )

    client.upload_csv("company_fleet_2024.csv")

Common Errors

400 Bad Request - Missing file_name

{
  "detail": "Field required",
  "field": "file_name"
}
Solution: Include the 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 (type, ownership, country, is_known) and correct format.

422 Validation Error - Invalid Vehicle ID

Cause: known_vehicle_id doesn’t exist in the known vehicles database Solution: Use the Known Vehicles endpoint to get valid vehicle IDs, or set is_known: false to create a custom vehicle.

Limits and Recommendations

LimitValue
Maximum file size100 MB
Maximum rows per CSV10,000
URL expiration time15 minutes
Processing time~1 second per 50 vehicles
For very large fleets (>10k vehicles), consider splitting them into multiple smaller CSVs.

Best Practices

Data Preparation

  1. Clean license plates: Remove special characters, use consistent format
  2. Validate vehicle IDs: Check that known_vehicle_id exists before upload
  3. Consistent naming: Use standardized vehicle names (e.g., β€œVan #1”, β€œVan #2”)
  4. Complete data: Include as many optional fields as possible for better reporting

Error Handling

After upload, check for vehicles with status: "error":
def check_for_errors():
    """Check if any vehicles failed to process"""
    response = requests.get(
        "https://api.dcycle.io/api/v1/vehicles",
        headers=headers,
        params={"filter_by": "status:error"}
    )

    vehicles = response.json()

    if vehicles['total'] > 0:
        print(f"⚠️ {vehicles['total']} vehicles have errors:")
        for vehicle in vehicles['items']:
            print(f"  - {vehicle.get('name', 'Unnamed')}: {vehicle.get('error_messages')}")

    return vehicles['total']

# Check after processing
errors_count = check_for_errors()
if errors_count == 0:
    print("βœ… All vehicles processed successfully")

Incremental Updates

To update existing vehicles or add new ones:
  1. Export current fleet to CSV
  2. Modify or add rows
  3. Re-upload (existing vehicles will be updated by license plate)

Special Notes

Known vs Unknown Vehicles

Known Vehicles (is_known: true):
  • Must provide valid known_vehicle_id
  • Emission factors are based on manufacturer data
  • Includes detailed specifications (engine size, power, etc.)
Unknown Vehicles (is_known: false):
  • System creates generic vehicle entry
  • Emission factors based on vehicle type and fuel
  • Less accurate but flexible for custom vehicles

Vehicle Status After Upload

Vehicles created via CSV will have:
  • status: "active" if successful, "error" if validation failed
  • co2e: 0.0 initially (updated when consumption data is added)
  • created_at: Upload timestamp

Adding Consumption Data

After creating vehicles, add fuel consumption data using the Create Invoice endpoint with type: "recharge" for electric vehicles or facility fuels for combustion vehicles.

Country Codes

Use ISO 3166-1 alpha-2 country codes:
  • Spain: ES
  • France: FR
  • Germany: DE
  • United Kingdom: GB
  • United States: US