JupiterOne Python SDK
A Python library for the JupiterOne API.
This SDK is open source. View the code, report issues, and see releases on GitHub: JupiterOne/jupiterone-api-client-python. The package is also available on PyPI.
Installation
Requires Python 3.6+
pip install jupiterone
Usage
Create a new client
from jupiterone import JupiterOneClient
j1 = JupiterOneClient(
account='<yourAccountId>',
token='<yourApiToken>',
url='https://graphql.us.jupiterone.io',
sync_url='https://api.us.jupiterone.io'
)
Regional or Custom Tenant Support
For users with J1 accounts in the EU region for example, the 'url' parameter will need to be updated to "https://graphql.eu.jupiterone.io" and the 'sync_url' parameter will need to be updated to "https://api.eu.jupiterone.io".
If no 'url' parameter is passed, the default of "https://graphql.us.jupiterone.io" is used, and if no 'sync_url' parameter is passed, the default of "https://api.us.jupiterone.io" is used.
Method Examples
See the examples directory in the SDK repository for comprehensive usage examples:
Core API Examples
examples/01_client_setup_and_queries.py- Client setup and basic J1QL queriesexamples/02_entity_management.py- Entity creation, updates, and deletionexamples/03_relationship_management.py- Relationship management and traversalexamples/examples.py- General API usage examples and patterns
Integration & Management
examples/04_integration_management.py- Integration instance management and sync jobsexamples/05_alert_rules_and_smartclasses.py- Alert rules and SmartClass operationsexamples/06_advanced_operations.py- Advanced API operations and workflows
Specialized Features
examples/07_account_parameters_list_example.py- Account parameter managementexamples/08_questions_management.py- Complete question management workflowsexamples/09_custom_file_transfer_example.py- Custom File Transfer (CFT) integration examplesexamples/create_integration_instance_example.py- Integration instance creation examples
Utility & Data Examples
examples/J1QLdeferredResponse.py- Deferred response query handling
Queries
Execute a query
# Basic query
QUERY = 'FIND Host'
query_result = j1.query_v1(query=QUERY)
# Including deleted entities
query_result = j1.query_v1(query=QUERY, include_deleted=True)
# Tree query
QUERY = 'FIND Host RETURN TREE'
query_result = j1.query_v1(query=QUERY)
# Complex query with properties
QUERY = 'FIND Host as h WITH platform = "linux" RETURN h.displayName, h.platform, h.ipAddress'
query_result = j1.query_v1(query=QUERY)
# Query with relationships
QUERY = 'FIND Host as h THAT HAS Application as a RETURN h.displayName, a.displayName'
query_result = j1.query_v1(query=QUERY)
# query_v1 uses cursor pagination by default and returns the full set of results
QUERY = "FIND (Device | Person)"
query_result = j1.query_v1(query=QUERY)
# Using limit and skip for pagination (deprecated: emits a DeprecationWarning, prefer cursor pagination)
QUERY = "FIND User"
limit_skip_result = j1.query_v1(query=QUERY, skip=0, limit=100)
# Using deferredResponse with J1QL to return large datasets
QUERY = "FIND UnifiedDevice"
deferred_response_query_r = j1.query_with_deferred_response(query=QUERY)
Entities
Create an entity
Note that the CreateEntity mutation behaves like an upsert, so a non-existent entity will be created or an existing entity will be updated.
import time
# Basic entity creation
properties = {
'myProperty': 'myValue',
'tag.myTagProperty': 'value_will_be_a_tag'
}
entity = j1.create_entity(
entity_key='my-unique-key',
entity_type='my_type',
entity_class='MyClass',
properties=properties
)
print(entity['entity'])
# Create entity with complex properties
complex_properties = {
'displayName': 'My Application Server',
'tag.Environment': 'production',
'tag.Team': 'engineering',
'ipAddress': '192.168.1.100',
'port': 8080,
'isActive': True
}
entity = j1.create_entity(
entity_key='app-server-001',
entity_type='application_server',
entity_class='Host',
properties=complex_properties
)
Update an existing entity
Only send in properties you want to add or update, other existing properties will not be modified.
# Basic property update
properties = {
'newProperty': 'newPropertyValue'
}
j1.update_entity(
entity_id='<id-of-entity-to-update>',
properties=properties
)
# Update with tags and complex properties
update_properties = {
'tag.Status': 'maintenance',
'lastUpdated': int(time.time()) * 1000,
'isActive': False
}
j1.update_entity(
entity_id='<id-of-entity-to-update>',
properties=update_properties
)
Delete an entity
# Delete by entity ID
j1.delete_entity(entity_id='<id-of-entity-to-delete>')
# Delete with timestamp and hard delete option
j1.delete_entity(
entity_id='<id-of-entity-to-delete>',
timestamp=int(time.time()) * 1000,
hard_delete=True # Set to False for soft delete
)
# Soft delete (entity marked as deleted but not permanently removed)
j1.delete_entity(
entity_id='<id-of-entity-to-delete>',
hard_delete=False
)
Relationships
Create a relationship
# Basic relationship creation
j1.create_relationship(
relationship_key='this_entity_relates_to_that_entity',
relationship_type='my_relationship_type',
relationship_class='MYRELATIONSHIP',
from_entity_id='<id-of-source-entity>',
to_entity_id='<id-of-destination-entity>'
)
# Create relationship with properties
j1.create_relationship(
relationship_key='<user-entity-id>:user_accesses_application:<application-entity-id>',
relationship_type='user_accesses_application',
relationship_class='ACCESSES',
from_entity_id='<user-entity-id>',
to_entity_id='<application-entity-id>',
properties={
'accessLevel': 'read',
'grantedOn': int(time.time()) * 1000,
'grantedBy': 'admin@company.com'
}
)
# Create relationship with complex properties
j1.create_relationship(
relationship_key='<host-entity-id>:host_installed_software:<software-entity-id>',
relationship_type='host_installed_software',
relationship_class='INSTALLED',
from_entity_id='<host-entity-id>',
to_entity_id='<software-entity-id>',
properties={
'installedOn': int(time.time()) * 1000,
'version': '2.1.0',
'installPath': '/usr/local/bin/software',
'permissions': ['read', 'execute']
}
)
Update a relationship
# Basic relationship update
j1.update_relationship(
relationship_id='<id-of-relationship-to-update>',
from_entity_id='<id-of-source-entity>',
to_entity_id='<id-of-destination-entity>',
properties={
"<relationship-property-name>": "<relationship-property-updated-value>",
},
)
# Update relationship with complex properties
j1.update_relationship(
relationship_id='<id-of-relationship-to-update>',
from_entity_id='<id-of-source-entity>',
to_entity_id='<id-of-destination-entity>',
properties={
'accessLevel': 'write',
'lastModified': int(time.time()) * 1000,
'modifiedBy': 'security_team',
'expiresOn': int(time.time() + 86400) * 1000 # 24 hours from now
}
)
# Update relationship with tags
j1.update_relationship(
relationship_id='<id-of-relationship-to-update>',
from_entity_id='<id-of-source-entity>',
to_entity_id='<id-of-destination-entity>',
properties={
'tag.Status': 'active',
'tag.Priority': 'high',
'tag.ReviewRequired': 'true'
}
)
# Update relationship with custom timestamp
j1.update_relationship(
relationship_id='<id-of-relationship-to-update>',
from_entity_id='<id-of-source-entity>',
to_entity_id='<id-of-destination-entity>',
properties={
'lastUpdated': int(time.time()) * 1000
},
timestamp=int(time.time()) * 1000 # Custom timestamp
)
Delete a relationship
# Delete a relationship (requires relationship ID, source entity ID, and target entity ID)
j1.delete_relationship(
relationship_id='<id-of-relationship-to-delete>',
from_entity_id='<id-of-source-entity>',
to_entity_id='<id-of-destination-entity>'
)
Entity Properties, Tags, and Raw Data
Fetch Graph Entity Properties
# Fetch all entity properties
properties = j1.fetch_all_entity_properties()
print(f"Found {len(properties)} entity properties")
# Properties are returned as a list of property objects
for prop in properties:
print(f"Property: {prop.get('name')} - Type: {prop.get('type')}")
Fetch Graph Entity Tags
# Fetch all entity tags
tags = j1.fetch_all_entity_tags()
print(f"Found {len(tags)} entity tags")
# Tags are returned as a list of tag objects
for tag in tags:
print(f"Tag: {tag.get('name')} - Values: {tag.get('values')}")
Fetch Entity Raw Data
# The entity's ingestion address is looked up automatically from the entity id
raw_data = j1.fetch_entity_raw_data_v2(entity_id='<id-of-entity>')
if raw_data['status'] == 'AVAILABLE':
print(raw_data['collectedAt'])
for item in raw_data['payload']:
print(item['name'], item['contentType'], item['data'])
else:
# NOT_AVAILABLE_OR_EXPIRED does not raise
print(raw_data['message'])
# For the fastest lookup, pass the entity's ingestion address to skip the extra lookup
j1.fetch_entity_raw_data_v2(
entity_id='<id-of-entity>',
source='<_source>',
integration_instance_id='<_integrationInstanceId>',
entity_key='<_key>',
entity_type='<_type>',
)
_idAlways pass the entity's _id metadata property as entity_id. Do not use an entity property
named id (or _key); those are different values and will not match the entity, so the call
returns NOT_AVAILABLE_OR_EXPIRED. To get it in a query, return _id, for example
FIND Host AS h RETURN h._id AS id.
fetch_entity_raw_data_v2 returns entityId, status (AVAILABLE or
NOT_AVAILABLE_OR_EXPIRED), message, collectedAt and payload (a list of name,
contentType and data). It is available in version 2.4.0 and later of the client.
fetch_entity_raw_data(entity_id=...) still works and returns the same response shape as
before (['data']['entityRawDataLegacy'], with status, message and collectedAt added),
but it is deprecated. Use fetch_entity_raw_data_v2.
Raw data is retrieved on demand from the sync job that produced it, and is retained for 7 days from that sync. An entity whose integration has not run in more than 7 days, or that was not included in its integration's most recent run, reports raw data as unavailable — the entity and its properties are unaffected. Retrieval reads from the ingestion pipeline rather than the graph, so expect modestly longer per-entity times when fetching in bulk. See What's new in Asset Data.
To call the underlying entityRawDataV2 GraphQL query directly, see
Entity raw data.
Integration Instances and Sync Jobs
Create Integration Instance
# Basic integration instance creation
instance = j1.create_integration_instance(
instance_name="AWS Production Account",
instance_description="Production AWS account integration"
)
print(f"Created instance: {instance['instance']['_id']}")
# Create integration instance with resource group assignment
instance = j1.create_integration_instance(
instance_name="AWS Development Account",
instance_description="Development AWS account integration",
resource_group_id="your-resource-group-id"
)
# Create integration instance with custom definition and resource group
instance = j1.create_integration_instance(
instance_name="Custom Integration",
instance_description="Custom integration for internal systems",
integration_definition_id="your-integration-definition-id",
resource_group_id="your-resource-group-id"
)
Start Synchronization Job
# Start sync job for an integration instance
sync_job = j1.start_sync_job(
instance_id=instance_id,
sync_mode="PATCH",
source="integration-external"
)
sync_job_id = sync_job['job'].get('id')
print(f"Started sync job: {sync_job_id}")
# The returned job ID is used for subsequent operations
job_id = sync_job_id
Upload Batch of Entities
# Prepare entities payload
entities_payload = [
{
"_key": "server-001",
"_type": "aws_ec2_instance",
"_class": "Host",
"displayName": "web-server-001",
"instanceId": "i-1234567890abcdef0",
"instanceType": "t3.micro",
"state": "running",
"tag.Environment": "production",
"tag.Team": "engineering"
},
{
"_key": "server-002",
"_type": "aws_ec2_instance",
"_class": "Host",
"displayName": "web-server-002",
"instanceId": "i-0987654321fedcba0",
"instanceType": "t3.small",
"state": "running",
"tag.Environment": "staging",
"tag.Team": "engineering"
},
{
"_key": "database-001",
"_type": "aws_rds_instance",
"_class": "Database",
"displayName": "prod-database",
"dbInstanceIdentifier": "prod-db",
"engine": "postgres",
"dbInstanceClass": "db.t3.micro",
"tag.Environment": "production",
"tag.Team": "data"
}
]
# Upload entities batch
result = j1.upload_entities_batch_json(
instance_job_id='<id-of-integration-sync-job>',
entities_list=entities_payload
)
print(f"Uploaded {len(entities_payload)} entities")
Upload Batch of Relationships
# Prepare relationships payload
relationships_payload = [
{
"_key": "server-001:aws_ec2_instance_connects_aws_rds_instance:database-001",
"_class": "CONNECTS",
"_type": "aws_ec2_instance_connects_aws_rds_instance",
"_fromEntityKey": "server-001",
"_toEntityKey": "database-001",
"port": 5432,
"protocol": "tcp",
"encrypted": True
},
{
"_key": "server-002:aws_ec2_instance_connects_aws_rds_instance:database-001",
"_class": "CONNECTS",
"_type": "aws_ec2_instance_connects_aws_rds_instance",
"_fromEntityKey": "server-002",
"_toEntityKey": "database-001",
"port": 5432,
"protocol": "tcp",
"encrypted": True
},
{
"_key": "user-001:aws_iam_user_owns_aws_ec2_instance:server-001",
"_class": "OWNS",
"_type": "aws_iam_user_owns_aws_ec2_instance",
"_fromEntityKey": "user-001",
"_toEntityKey": "server-001",
"ownershipType": "creator"
}
]
# Upload relationships batch
result = j1.upload_relationships_batch_json(
instance_job_id='<id-of-integration-sync-job>',
relationships_list=relationships_payload
)
print(f"Uploaded {len(relationships_payload)} relationships")
Upload Batch of Entities and Relationships
# Prepare combined payload
combined_payload = {
"entities": [
{
"_key": "vpc-001",
"_type": "aws_vpc",
"_class": "Network",
"displayName": "production-vpc",
"vpcId": "vpc-12345678",
"cidrBlock": "10.0.0.0/16",
"state": "available",
"tag.Environment": "production",
"tag.Purpose": "web_servers"
},
{
"_key": "subnet-001",
"_type": "aws_subnet",
"_class": "Network",
"displayName": "public-subnet-1a",
"subnetId": "subnet-12345678",
"cidrBlock": "10.0.1.0/24",
"availabilityZone": "us-east-1a",
"state": "available"
}
],
"relationships": [
{
"_key": "vpc-001:aws_vpc_contains_aws_subnet:subnet-001",
"_class": "CONTAINS",
"_type": "aws_vpc_contains_aws_subnet",
"_fromEntityKey": "vpc-001",
"_toEntityKey": "subnet-001"
},
{
"_key": "subnet-001:aws_subnet_contains_aws_ec2_instance:server-001",
"_class": "CONTAINS",
"_type": "aws_subnet_contains_aws_ec2_instance",
"_fromEntityKey": "subnet-001",
"_toEntityKey": "server-001"
}
]
}
# Upload combined batch
result = j1.upload_combined_batch_json(
instance_job_id='<id-of-integration-sync-job>',
combined_payload=combined_payload
)
print(f"Uploaded {len(combined_payload['entities'])} entities and {len(combined_payload['relationships'])} relationships")
Abort Synchronization Job
# Abort the sync job
result = j1.abort_sync_job(instance_job_id='<id-of-integration-sync-job>')
print(f"Abort sync job: {result['status'].get('id')}")
# Check job status
if result['job']['status'] == 'ABORTED':
print("Sync job Abort successfully")
Finalize Synchronization Job
# Finalize the sync job
result = j1.finalize_sync_job(instance_job_id='<id-of-integration-sync-job>')
print(f"Finalized sync job: {result['job'].get('id')}")
# Check job status
if result['job']['status'] == 'COMPLETED':
print("Sync job completed successfully")
elif result['job']['status'] == 'FAILED':
print(f"Sync job failed: {result['job'].get('error', 'Unknown error')}")
Custom File Transfer (CFT) Integration Methods
# Get a pre-signed URL for file upload
upload_info = j1.get_cft_upload_url(
integration_instance_id='<id-of-integration-instance>',
filename='data.csv',
dataset_id='<id-of-dataset>'
)
print(f"Upload URL: {upload_info['uploadUrl']}")
print(f"Expires at: {upload_info['expiresAt']}")
# Upload a CSV file to the CFT integration
upload_result = j1.upload_cft_file(
upload_url=upload_info['uploadUrl'],
file_path='/path/to/your/data.csv'
)
print(f"Upload status: {upload_result['status_code']}")
print(f"Upload success: {upload_result['success']}")
# Invoke the CFT integration to process the uploaded file
invoke_result = j1.invoke_cft_integration(
integration_instance_id='<id-of-integration-instance>'
)
if invoke_result is True:
print("CFT integration invoked successfully")
elif invoke_result == 'ALREADY_RUNNING':
print("CFT integration is already running")
else:
print("Failed to invoke CFT integration")
# Complete workflow example
def upload_and_process_data(j1, instance_id, dataset_id, file_path):
"""Complete workflow for CFT data upload and processing"""
try:
# Step 1: Get upload URL
upload_info = j1.get_cft_upload_url(instance_id, 'data.csv', dataset_id)
# Step 2: Upload file
upload_result = j1.upload_cft_file(upload_info['uploadUrl'], file_path)
if not upload_result['success']:
raise Exception(f"Upload failed: {upload_result['status_code']}")
# Step 3: Invoke processing
invoke_result = j1.invoke_cft_integration(instance_id)
if invoke_result is True:
print("Data uploaded and processing started successfully")
else:
print(f"Processing status: {invoke_result}")
except Exception as e:
print(f"Error in CFT workflow: {e}")
# Usage
upload_and_process_data(j1, 'instance-123', 'dataset-456', '/path/to/data.csv')
Fetch Integration Instance Jobs
# Fetch all jobs for an integration instance
jobs = j1.fetch_integration_jobs(instance_id='<id-of-integration-instance>')
print(f"Found {len(jobs)} jobs for instance")
# Process job information
for job in jobs:
print(f"Job ID: {job['_id']}")
print(f"Status: {job['status']}")
print(f"Started: {job.get('startedOn')}")
print(f"Completed: {job.get('completedOn')}")
print("---")
Fetch Integration Instance Job Events
# Fetch events for a specific job
events = j1.fetch_integration_job_events(
instance_id='<id-of-integration-instance>',
instance_job_id='<id-of-integration-instance-job>'
)
print(f"Found {len(events)} events for job")
# Process event information
for event in events:
print(f"Event: {event.get('event')}")
print(f"Timestamp: {event.get('timestamp')}")
print(f"Message: {event.get('message')}")
print("---")