launchd runs program every hour even though StartCalendarInterval array is provided
launchd is throwing out 24 as an invalid Hour value, thus interpreting that entire entry as Minute = 0. This causes your agent to run every hour on the dot.

The correct value for midnight is 0:

&lt;dict&gt;
    &lt;key&gt;Hour&lt;/key&gt;
    &lt;integer&gt;0&lt;/integer&gt;

    &lt;key&gt;Minute&lt;/key&gt;
    &lt;integer&gt;0&lt;/integer&gt;
&lt;/dict&gt;