How to find number of animated frames in a scene via python?
If the duration of the scene is known, simply use bpy.context.scene.frame_end and assign a custom duration value to it - this will overwrite the current playback/rendering range:

import bpy

# get the current scene
scn = bpy.context.scene 

# assign new duration value
scn.frame_end = 880




In order to adjust the scene duration dynamically (based on the included animations) you'll need to iterate through all the relevant objects in the scene  and read their keyframes by using either Animation Data or Actions.

Keyframes via Animation Data

Use animation_data to get the keyframes of an object. Following example reads the keyframes of the selected objects and prints the first and the last keyframe to the console.

import bpy
import math

# get keyframes of object list
def get_keyframes(obj_list):
    keyframes = []
    for obj in obj_list:
        anim = obj.animation_data
        if anim is not None and anim.action is not None:
            for fcu in anim.action.fcurves:
                for keyframe in fcu.keyframe_points:
                    x, y = keyframe.co
                    if x not in keyframes:
                        keyframes.append((math.ceil(x)))
    return keyframes

# get all selected objects
selection = bpy.context.selected_objects

# check if selection is not empty
if selection:

    # get all frames with assigned keyframes
    keys = get_keyframes(selection)

    # print all keyframes
    print (keys)   

    # print first and last keyframe
    print ("{} {}".format("first keyframe:", keys[0]))
    print ("{} {}".format("last keyframe:", keys[-1]))

else:
    print ('nothing selected')


Keyframes via Actions

Use bpy.data.actions to get a list of actions in the scene, but actions are not associated with objects, scenes or nodes. Following example reads the frame range of all F-Curves within the actions of the file and prints the first and the last keyframe to the console.

import bpy

# check if actions is empty
if bpy.data.actions:

    # get all actions
    action_list = [action.frame_range for action in bpy.data.actions]

    # sort, remove doubles and create a set
    keys = (sorted(set([item for sublist in action_list for item in sublist])))

    # print all keyframes
    print (keys)

    # print first and last keyframe
    print ("{} {}".format("first keyframe:", keys[0]))
    print ("{} {}".format("last keyframe:", keys[-1]))

else:
    print ("no actions")


Test scene



Console Output of both methods:

[1, 5, 7, 18, 21, 30, 47, 49, 67, 69, 75, 84, 100]
first keyframe: 1
last keyframe: 100


Speed comparison


Animation Data method takes 0.000446319580078125 sec.
Action method takes 0.00011038780212402344 sec.


Adjusting the timeline

As mentioned above, in order to set the beginning and end of the scene, you can assign the calculated values to bpy.context.scene.frame_start and bpy.context.scene.frame_end:

# get the current scene
scn = bpy.context.scene 

# assign new starting frame
scn.frame_start = keys[0]

# assign new end frame
scn.frame_end = keys[-1]




Final Note

It really depends on what you want to achieve. Using animation_data is slower, but using action.frame_range can be dangerous, because if you have to delete an object during the process, the actions of the objects remain until the blend file is saved again. See: https://blender.stackexchange.com/a/27235/3710