Python API: idea to provide access to conformance checking results


We’re working with the Celonis Python API and were asking ourselves where Celonis would add value instead of directly working on the data in Python. We mainly see benefits in the PQL statements you can run, and then specifically the process related queries you can run.

However, I was wondering if the Pyton API could also provide access to the conformance checking results. This would enable additional analysis in Python, where the conformance checking results are hard/impossible to recreate in Python without Celonis.

Any thoughts on this?



Hi Joos,

very good point, I also think this would add value to the Python API!

I would be very interested if you already have an Idea on how the results of - let’s say - a get_conformance() python call should look like? Can you give an example of what you would like to see as a result?

Currently, for example, it would already be possible to get a dataframe in python that, for each case, contains a flag whether the case conforms or not.

Thanks for your post and looking forward to your ideas on that!


1 Like


Hi David,

Thanks for your quick reply.

I see several aspects:

  1. Case level OK/NOK
  2. Activity level OK/NOK
  3. Case level ‘type of deviation’ label (as the dashboard also groups similar deviations and provides a percentage)
  4. Per deviation ‘type’ the difference in #steps and throughput time, like in the dashboard

The main usage would be to find root causes of deviations (in addition to what Celonis already provides), and/or to use the conformance checking results to predict remaining runtime of a case.

You mention that my #1 would already be possible, although the Python API documentation does not mention this. Do I need to fetch the Conformance checking sheet from the dashboard and work with that? If you have a basic example that would be much appreciated!

1 Like


Hi Joos,

thanks for your suggestions and Ideas! I noted them and see what we can do!

So Case level OK/NOK and Activity level OK/NOK is possible, however, there is no specific python functionality for that yet. Let me quickly explain what you could do:

There is a CONFORMANCE PQL operator which takes the model as an input and, for each activity, labels the violation. It is not documented because it is usually only used by the conformance component and not really meant to be used in other components. The labels are just numbers, so currently you are not able to determine the actual violation, but the label “0” means “OK”. So you could check the result of the CONFORMANCE operator in a CASE WHEN - and return “OK” when the result is “0”, and “NOK” otherwise. For case level, you can aggregate the result. We are currently working on a “Conformance-To-String” operator which can be used to convert the labels into their string representation, but I cannot promise when this will be released.

If you want to use the operator, the best way to do it is to open the Conformance checker sheet, and then take a look at the log entries of your CPM4 instance. You will see the CONFORMANCE() operator query, which you can copy and paste into an OLAP table query next to the Activity Column in the frontend (type it yourself is hard because you need to pass the model to the operator).

You can then either retrieve the data from the OLAP table via python, or write the query directly inside your python code.

I agree that it’s a bit hacky, but I’m sure you can make it work!




Hi David,

(I’m not afraid of a bit hacky)

Thanks a lot for your reply! Very good that we can discover these undocumented features through this community!
I’ll try to ask our system admin (after the 4.5 upgrade) to let me have a look at the log. I’m (indeed) particularly curious how I pass ‘the model’ as an input, and whether this is something I might be able to reverse-engineer :D. I hope I have time to go and find this out in the coming week(s). Once I have answers I’ll report them back here. Thanks for thinking with me :slight_smile:

1 Like


Looking forward to your results! Should we develop a less hacky solution, it will be also documented then.


(ps: instead of consulting the logs, you can also look at the network request params that are sent when you open the conformance sheet using your browser’s network traffic console and find the CONFORMANCE operator, which is even more hacky :wink: )

1 Like


Cool idea, and no dependency on a system admin. :smiley:
I already found the network monitor in Firefox (Ctrl+Shift+E) and got my hands on a conformance statement. Will dissect it now :slight_smile:

1 Like


Great, just let me know if any questions occur! Excited to hear your feedback on the result.

1 Like


After a lot of hacking, making mistakes, and doing other stuff, I managed to get the following bit of code running on the attached datasetArtificial - Loan Process.csv (25.8 KB)

#Conformance PQL:
    conf_PQL_base = "CONFORMANCE(\"Artificial - Loan Process.csv\".\"concept:name\", [\"P_0\" \"P_1\" \"P_2\" \"P_3\" \"P_4\" \"P_5\" \"P_6\" \"P_7\" \"P_8\" \"P_9\" \"P_10\" \"P_11\" \"P_12\" \"P_13\" \"P_14\" \"P_15\" \"P_19\" \"P_54\" \"P_64\"], [\"T_16\" \"T_20\" \"T_23\" \"T_26\" \"T_29\" \"T_32\" \"T_35\" \"T_38\" \"T_41\" \"T_44\" \"T_49\" \"T_55\" \"T_58\" \"T_61\" \"T_65\" \"T_68\" \"T_71\"], [[\"P_15\" \"T_16\"] [\"T_16\" \"P_0\"] [\"T_20\" \"P_19\"] [\"P_14\" \"T_20\"] [\"P_0\" \"T_23\"] [\"T_23\" \"P_1\"] [\"P_2\" \"T_26\"] [\"T_26\" \"P_7\"] [\"P_3\" \"T_29\"] [\"T_29\" \"P_6\"] [\"P_4\" \"T_32\"] [\"T_32\" \"P_5\"] [\"P_9\" \"T_35\"] [\"T_35\" \"P_12\"] [\"P_10\" \"T_38\"] [\"T_38\" \"P_11\"] [\"P_13\" \"T_41\"] [\"T_41\" \"P_14\"] [\"P_1\" \"T_44\"] [\"T_44\" \"P_2\"] [\"T_44\" \"P_3\"] [\"T_44\" \"P_4\"] [\"P_5\" \"T_49\"] [\"P_6\" \"T_49\"] [\"P_7\" \"T_49\"] [\"T_49\" \"P_8\"] [\"T_55\" \"P_54\"] [\"P_8\" \"T_55\"] [\"P_54\" \"T_58\"] [\"T_58\" \"P_9\"] [\"P_54\" \"T_61\"] [\"T_61\" \"P_10\"] [\"T_65\" \"P_64\"] [\"P_11\" \"T_65\"] [\"T_68\" \"P_64\"] [\"P_12\" \"T_68\"] [\"P_64\" \"T_71\"] [\"T_71\" \"P_13\"]], [['register application' \"T_23\"] ['calculate capacity' \"T_26\"] ['check credit' \"T_29\"] ['check system' \"T_32\"] ['accept' \"T_35\"] ['reject' \"T_38\"] ['send decision e-mail' \"T_41\"]], [\"P_15\"], [\"P_19\"])"
    q = Query()
    q.add(Dimension('"Artificial - Loan Process.csv"."Case-Id"', 'CaseID'))
    q.add(Dimension('"Artificial - Loan Process.csv"."Concept:name"', 'Activity'))
    q.add(Dimension(conf_PQL_base, 'CONFORMANCE'))
    conf_result = datamodel.query(q)

It returns me a list of CaseID, Activity name and whether it is conformant (=0) or not (>0, for me for instance ‘12884901894’ and other values).

The conformance PQL operator seems to take the following arguments:

  1. Activity name table & column
  2. list of places of the Petri net
  3. list of transitions of the Petri net
  4. List of place-transition or transition-place pairs (in list shape)
  5. List of meaningful names for each transition to map them to the data
  6. Start place of the Petri net (source place)
  7. End place of the Petri net (sink place)

I’m only assuming the implementation behind this is the token-based replay or similar, given the signature of the conformance PQL function and its results (see

Very cool to understand this, and thus to be able to build Petri nets in Python, convert them and run them through Celonis, and then inspect the result.
Now back to the real work and then weekend :wink: :smiley:



You are absolutely right, the arguments define a petri net, which is then used for token replay.
Now I don’t want to hide what the numbers mean :smiley: :
A positive number encodes the “X is followed by Y” violation. Negative numbers are also possible, that means “X is an undesired activity”. And the number 2147483647 marks an incomplete case.
Activities are encoded in these numbers using internal IDs, which might change after an DM reload, so this is the only thing that you currently cannot decode unfortunately.

Have a great weekend!